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);
143 u
->core
->mainloop
->time_free(u
->time_event
);
144 u
->time_event
= NULL
;
148 pa_sink_input_unlink(u
->sink_input
);
150 if (u
->source_output
)
151 pa_source_output_unlink(u
->source_output
);
154 pa_sink_input_unref(u
->sink_input
);
155 u
->sink_input
= NULL
;
158 if (u
->source_output
) {
159 pa_source_output_unref(u
->source_output
);
160 u
->source_output
= NULL
;
164 /* Called from main context */
165 static void adjust_rates(struct userdata
*u
) {
167 uint32_t old_rate
, base_rate
, new_rate
;
168 pa_usec_t buffer_latency
;
171 pa_assert_ctl_context();
173 pa_asyncmsgq_send(u
->source_output
->source
->asyncmsgq
, PA_MSGOBJECT(u
->source_output
), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
, NULL
, 0, NULL
);
174 pa_asyncmsgq_send(u
->sink_input
->sink
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
, NULL
, 0, NULL
);
177 u
->latency_snapshot
.sink_input_buffer
+
178 u
->latency_snapshot
.source_output_buffer
;
180 if (u
->latency_snapshot
.recv_counter
<= u
->latency_snapshot
.send_counter
)
181 buffer
+= (size_t) (u
->latency_snapshot
.send_counter
- u
->latency_snapshot
.recv_counter
);
183 buffer
+= PA_CLIP_SUB(buffer
, (size_t) (u
->latency_snapshot
.recv_counter
- u
->latency_snapshot
.send_counter
));
185 buffer_latency
= pa_bytes_to_usec(buffer
, &u
->sink_input
->sample_spec
);
187 pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
188 (double) u
->latency_snapshot
.sink_latency
/ PA_USEC_PER_MSEC
,
189 (double) buffer_latency
/ PA_USEC_PER_MSEC
,
190 (double) u
->latency_snapshot
.source_latency
/ PA_USEC_PER_MSEC
,
191 ((double) u
->latency_snapshot
.sink_latency
+ buffer_latency
+ u
->latency_snapshot
.source_latency
) / PA_USEC_PER_MSEC
);
193 pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes",
194 u
->latency_snapshot
.max_request
*2,
195 u
->latency_snapshot
.min_memblockq_length
);
197 fs
= pa_frame_size(&u
->sink_input
->sample_spec
);
198 old_rate
= u
->sink_input
->sample_spec
.rate
;
199 base_rate
= u
->source_output
->sample_spec
.rate
;
201 if (u
->latency_snapshot
.min_memblockq_length
< u
->latency_snapshot
.max_request
*2)
202 new_rate
= base_rate
- (((u
->latency_snapshot
.max_request
*2 - u
->latency_snapshot
.min_memblockq_length
) / fs
) *PA_USEC_PER_SEC
)/u
->adjust_time
;
204 new_rate
= base_rate
+ (((u
->latency_snapshot
.min_memblockq_length
- u
->latency_snapshot
.max_request
*2) / fs
) *PA_USEC_PER_SEC
)/u
->adjust_time
;
206 if (new_rate
< (uint32_t) (base_rate
*0.8) || new_rate
> (uint32_t) (base_rate
*1.25)) {
207 pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate
, new_rate
);
208 new_rate
= base_rate
;
210 if (base_rate
< new_rate
+ 20 && new_rate
< base_rate
+ 20)
211 new_rate
= base_rate
;
212 /* Do the adjustment in small steps; 2‰ can be considered inaudible */
213 if (new_rate
< (uint32_t) (old_rate
*0.998) || new_rate
> (uint32_t) (old_rate
*1.002)) {
214 pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate
, old_rate
);
215 new_rate
= PA_CLAMP(new_rate
, (uint32_t) (old_rate
*0.998), (uint32_t) (old_rate
*1.002));
219 pa_sink_input_set_rate(u
->sink_input
, new_rate
);
220 pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u
->sink_input
->sink
->name
, (unsigned long) new_rate
);
222 pa_core_rttime_restart(u
->core
, u
->time_event
, pa_rtclock_now() + u
->adjust_time
);
225 /* Called from main context */
226 static void time_callback(pa_mainloop_api
*a
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
227 struct userdata
*u
= userdata
;
231 pa_assert(u
->time_event
== e
);
236 /* Called from input thread context */
237 static void source_output_push_cb(pa_source_output
*o
, const pa_memchunk
*chunk
) {
241 pa_source_output_assert_ref(o
);
242 pa_source_output_assert_io_context(o
);
243 pa_assert_se(u
= o
->userdata
);
245 if (u
->skip
> chunk
->length
) {
246 u
->skip
-= chunk
->length
;
252 copy
.index
+= u
->skip
;
253 copy
.length
-= u
->skip
;
259 pa_asyncmsgq_post(u
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_POST
, NULL
, 0, chunk
, NULL
);
260 u
->send_counter
+= (int64_t) chunk
->length
;
263 /* Called from input thread context */
264 static void source_output_process_rewind_cb(pa_source_output
*o
, size_t nbytes
) {
267 pa_source_output_assert_ref(o
);
268 pa_source_output_assert_io_context(o
);
269 pa_assert_se(u
= o
->userdata
);
271 pa_asyncmsgq_post(u
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_REWIND
, NULL
, (int64_t) nbytes
, NULL
, NULL
);
272 u
->send_counter
-= (int64_t) nbytes
;
275 /* Called from output thread context */
276 static int source_output_process_msg_cb(pa_msgobject
*obj
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
277 struct userdata
*u
= PA_SOURCE_OUTPUT(obj
)->userdata
;
281 case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
: {
284 length
= pa_memblockq_get_length(u
->source_output
->thread_info
.delay_memblockq
);
286 u
->latency_snapshot
.send_counter
= u
->send_counter
;
287 u
->latency_snapshot
.source_output_buffer
= u
->source_output
->thread_info
.resampler
? pa_resampler_result(u
->source_output
->thread_info
.resampler
, length
) : length
;
288 u
->latency_snapshot
.source_latency
= pa_source_get_latency_within_thread(u
->source_output
->source
);
294 return pa_source_output_process_msg(obj
, code
, data
, offset
, chunk
);
297 /* Called from output thread context */
298 static void source_output_attach_cb(pa_source_output
*o
) {
301 pa_source_output_assert_ref(o
);
302 pa_source_output_assert_io_context(o
);
303 pa_assert_se(u
= o
->userdata
);
305 u
->rtpoll_item_write
= pa_rtpoll_item_new_asyncmsgq_write(
306 o
->source
->thread_info
.rtpoll
,
311 /* Called from output thread context */
312 static void source_output_detach_cb(pa_source_output
*o
) {
315 pa_source_output_assert_ref(o
);
316 pa_source_output_assert_io_context(o
);
317 pa_assert_se(u
= o
->userdata
);
319 if (u
->rtpoll_item_write
) {
320 pa_rtpoll_item_free(u
->rtpoll_item_write
);
321 u
->rtpoll_item_write
= NULL
;
325 /* Called from output thread context */
326 static void source_output_state_change_cb(pa_source_output
*o
, pa_source_output_state_t state
) {
329 pa_source_output_assert_ref(o
);
330 pa_source_output_assert_io_context(o
);
331 pa_assert_se(u
= o
->userdata
);
333 if (PA_SOURCE_OUTPUT_IS_LINKED(state
) && o
->thread_info
.state
== PA_SOURCE_OUTPUT_INIT
) {
335 u
->skip
= pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o
->source
),
339 pa_log_info("Skipping %lu bytes", (unsigned long) u
->skip
);
343 /* Called from main thread */
344 static void source_output_kill_cb(pa_source_output
*o
) {
347 pa_source_output_assert_ref(o
);
348 pa_assert_ctl_context();
349 pa_assert_se(u
= o
->userdata
);
352 pa_module_unload_request(u
->module
, TRUE
);
355 /* Called from main thread */
356 static pa_bool_t
source_output_may_move_to_cb(pa_source_output
*o
, pa_source
*dest
) {
359 pa_source_output_assert_ref(o
);
360 pa_assert_ctl_context();
361 pa_assert_se(u
= o
->userdata
);
363 return dest
!= u
->sink_input
->sink
->monitor_source
;
366 /* Called from main thread */
367 static void source_output_moving_cb(pa_source_output
*o
, pa_source
*dest
) {
375 pa_source_output_assert_ref(o
);
376 pa_assert_ctl_context();
377 pa_assert_se(u
= o
->userdata
);
379 p
= pa_proplist_new();
380 pa_proplist_setf(p
, PA_PROP_MEDIA_NAME
, "Loopback of %s", pa_strnull(pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
382 if ((n
= pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
383 pa_proplist_sets(p
, PA_PROP_MEDIA_ICON_NAME
, n
);
385 pa_sink_input_update_proplist(u
->sink_input
, PA_UPDATE_REPLACE
, p
);
389 /* Called from output thread context */
390 static void update_min_memblockq_length(struct userdata
*u
) {
394 pa_sink_input_assert_io_context(u
->sink_input
);
396 length
= pa_memblockq_get_length(u
->memblockq
);
398 if (u
->min_memblockq_length
== (size_t) -1 ||
399 length
< u
->min_memblockq_length
)
400 u
->min_memblockq_length
= length
;
403 /* Called from output thread context */
404 static int sink_input_pop_cb(pa_sink_input
*i
, size_t nbytes
, pa_memchunk
*chunk
) {
407 pa_sink_input_assert_ref(i
);
408 pa_sink_input_assert_io_context(i
);
409 pa_assert_se(u
= i
->userdata
);
413 while (pa_asyncmsgq_process_one(u
->asyncmsgq
) > 0)
417 if (pa_memblockq_peek(u
->memblockq
, chunk
) < 0) {
418 pa_log_info("Could not peek into queue");
422 chunk
->length
= PA_MIN(chunk
->length
, nbytes
);
423 pa_memblockq_drop(u
->memblockq
, chunk
->length
);
425 update_min_memblockq_length(u
);
430 /* Called from output thread context */
431 static void sink_input_process_rewind_cb(pa_sink_input
*i
, size_t nbytes
) {
434 pa_sink_input_assert_ref(i
);
435 pa_sink_input_assert_io_context(i
);
436 pa_assert_se(u
= i
->userdata
);
438 pa_memblockq_rewind(u
->memblockq
, nbytes
);
441 /* Called from output thread context */
442 static int sink_input_process_msg_cb(pa_msgobject
*obj
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
443 struct userdata
*u
= PA_SINK_INPUT(obj
)->userdata
;
447 case PA_SINK_INPUT_MESSAGE_GET_LATENCY
: {
450 pa_sink_input_assert_io_context(u
->sink_input
);
452 *r
= pa_bytes_to_usec(pa_memblockq_get_length(u
->memblockq
), &u
->sink_input
->sample_spec
);
454 /* Fall through, the default handler will add in the extra
455 * latency added by the resampler */
459 case SINK_INPUT_MESSAGE_POST
:
461 pa_sink_input_assert_io_context(u
->sink_input
);
463 if (PA_SINK_IS_OPENED(u
->sink_input
->sink
->thread_info
.state
))
464 pa_memblockq_push_align(u
->memblockq
, chunk
);
466 pa_memblockq_flush_write(u
->memblockq
, TRUE
);
468 update_min_memblockq_length(u
);
470 /* Is this the end of an underrun? Then let's start things
473 u
->sink_input
->thread_info
.underrun_for
> 0 &&
474 pa_memblockq_is_readable(u
->memblockq
)) {
476 pa_log_debug("Requesting rewind due to end of underrun.");
477 pa_sink_input_request_rewind(u
->sink_input
,
478 (size_t) (u
->sink_input
->thread_info
.underrun_for
== (size_t) -1 ? 0 : u
->sink_input
->thread_info
.underrun_for
),
482 u
->recv_counter
+= (int64_t) chunk
->length
;
486 case SINK_INPUT_MESSAGE_REWIND
:
488 pa_sink_input_assert_io_context(u
->sink_input
);
490 if (PA_SINK_IS_OPENED(u
->sink_input
->sink
->thread_info
.state
))
491 pa_memblockq_seek(u
->memblockq
, -offset
, PA_SEEK_RELATIVE
, TRUE
);
493 pa_memblockq_flush_write(u
->memblockq
, TRUE
);
495 u
->recv_counter
-= offset
;
497 update_min_memblockq_length(u
);
501 case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
: {
504 update_min_memblockq_length(u
);
506 length
= pa_memblockq_get_length(u
->sink_input
->thread_info
.render_memblockq
);
508 u
->latency_snapshot
.recv_counter
= u
->recv_counter
;
509 u
->latency_snapshot
.sink_input_buffer
=
510 pa_memblockq_get_length(u
->memblockq
) +
511 (u
->sink_input
->thread_info
.resampler
? pa_resampler_request(u
->sink_input
->thread_info
.resampler
, length
) : length
);
512 u
->latency_snapshot
.sink_latency
= pa_sink_get_latency_within_thread(u
->sink_input
->sink
);
514 u
->latency_snapshot
.max_request
= pa_sink_input_get_max_request(u
->sink_input
);
516 u
->latency_snapshot
.min_memblockq_length
= u
->min_memblockq_length
;
517 u
->min_memblockq_length
= (size_t) -1;
522 case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
: {
523 /* This message is sent from the IO thread to the main
524 * thread! So don't be confused. All the user cases above
525 * are executed in thread context, but this one is not! */
527 pa_assert_ctl_context();
529 if (u
->adjust_time
> 0)
535 return pa_sink_input_process_msg(obj
, code
, data
, offset
, chunk
);
538 /* Called from output thread context */
539 static void sink_input_attach_cb(pa_sink_input
*i
) {
542 pa_sink_input_assert_ref(i
);
543 pa_sink_input_assert_io_context(i
);
544 pa_assert_se(u
= i
->userdata
);
546 u
->rtpoll_item_read
= pa_rtpoll_item_new_asyncmsgq_read(
547 i
->sink
->thread_info
.rtpoll
,
551 pa_memblockq_set_prebuf(u
->memblockq
, pa_sink_input_get_max_request(i
)*2);
552 pa_memblockq_set_maxrewind(u
->memblockq
, pa_sink_input_get_max_rewind(i
));
554 u
->min_memblockq_length
= (size_t) -1;
557 /* Called from output thread context */
558 static void sink_input_detach_cb(pa_sink_input
*i
) {
561 pa_sink_input_assert_ref(i
);
562 pa_sink_input_assert_io_context(i
);
563 pa_assert_se(u
= i
->userdata
);
565 if (u
->rtpoll_item_read
) {
566 pa_rtpoll_item_free(u
->rtpoll_item_read
);
567 u
->rtpoll_item_read
= NULL
;
571 /* Called from output thread context */
572 static void sink_input_update_max_rewind_cb(pa_sink_input
*i
, size_t nbytes
) {
575 pa_sink_input_assert_ref(i
);
576 pa_sink_input_assert_io_context(i
);
577 pa_assert_se(u
= i
->userdata
);
579 pa_memblockq_set_maxrewind(u
->memblockq
, nbytes
);
582 /* Called from output thread context */
583 static void sink_input_update_max_request_cb(pa_sink_input
*i
, size_t nbytes
) {
586 pa_sink_input_assert_ref(i
);
587 pa_sink_input_assert_io_context(i
);
588 pa_assert_se(u
= i
->userdata
);
590 pa_memblockq_set_prebuf(u
->memblockq
, nbytes
*2);
591 pa_log_info("Max request changed");
592 pa_asyncmsgq_post(pa_thread_mq_get()->outq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
, NULL
, 0, NULL
, NULL
);
595 /* Called from main thread */
596 static void sink_input_kill_cb(pa_sink_input
*i
) {
599 pa_sink_input_assert_ref(i
);
600 pa_assert_ctl_context();
601 pa_assert_se(u
= i
->userdata
);
604 pa_module_unload_request(u
->module
, TRUE
);
607 /* Called from main thread */
608 static void sink_input_moving_cb(pa_sink_input
*i
, pa_sink
*dest
) {
616 pa_sink_input_assert_ref(i
);
617 pa_assert_ctl_context();
618 pa_assert_se(u
= i
->userdata
);
620 p
= pa_proplist_new();
621 pa_proplist_setf(p
, PA_PROP_MEDIA_NAME
, "Loopback to %s", pa_strnull(pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
623 if ((n
= pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
624 pa_proplist_sets(p
, PA_PROP_MEDIA_ICON_NAME
, n
);
626 pa_source_output_update_proplist(u
->source_output
, PA_UPDATE_REPLACE
, p
);
630 /* Called from main thread */
631 static pa_bool_t
sink_input_may_move_to_cb(pa_sink_input
*i
, pa_sink
*dest
) {
634 pa_sink_input_assert_ref(i
);
635 pa_assert_ctl_context();
636 pa_assert_se(u
= i
->userdata
);
638 if (!u
->source_output
->source
->monitor_of
)
641 return dest
!= u
->source_output
->source
->monitor_of
;
644 int pa__init(pa_module
*m
) {
645 pa_modargs
*ma
= NULL
;
648 pa_sink_input_new_data sink_input_data
;
649 pa_bool_t sink_dont_move
;
651 pa_source_output_new_data source_output_data
;
652 pa_bool_t source_dont_move
;
653 uint32_t latency_msec
;
657 uint32_t adjust_time_sec
;
659 pa_bool_t remix
= TRUE
;
663 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
664 pa_log("Failed to parse module arguments");
668 if (!(source
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "source", NULL
), PA_NAMEREG_SOURCE
))) {
669 pa_log("No such source.");
673 if (!(sink
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "sink", NULL
), PA_NAMEREG_SINK
))) {
674 pa_log("No such sink.");
678 if (pa_modargs_get_value_boolean(ma
, "remix", &remix
) < 0) {
679 pa_log("Invalid boolean remix parameter");
683 ss
= sink
->sample_spec
;
684 map
= sink
->channel_map
;
685 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
686 pa_log("Invalid sample format specification or channel map");
690 latency_msec
= DEFAULT_LATENCY_MSEC
;
691 if (pa_modargs_get_value_u32(ma
, "latency_msec", &latency_msec
) < 0 || latency_msec
< 1 || latency_msec
> 2000) {
692 pa_log("Invalid latency specification");
696 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
699 u
->latency
= (pa_usec_t
) latency_msec
* PA_USEC_PER_MSEC
;
701 adjust_time_sec
= DEFAULT_ADJUST_TIME_USEC
/ PA_USEC_PER_SEC
;
702 if (pa_modargs_get_value_u32(ma
, "adjust_time", &adjust_time_sec
) < 0) {
703 pa_log("Failed to parse adjust_time value");
707 if (adjust_time_sec
!= DEFAULT_ADJUST_TIME_USEC
/ PA_USEC_PER_SEC
)
708 u
->adjust_time
= adjust_time_sec
* PA_USEC_PER_SEC
;
710 u
->adjust_time
= DEFAULT_ADJUST_TIME_USEC
;
712 pa_sink_input_new_data_init(&sink_input_data
);
713 sink_input_data
.driver
= __FILE__
;
714 sink_input_data
.module
= m
;
715 pa_sink_input_new_data_set_sink(&sink_input_data
, sink
, FALSE
);
717 if (pa_modargs_get_proplist(ma
, "sink_input_properties", sink_input_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
718 pa_log("Failed to parse the sink_input_properties value.");
719 pa_sink_input_new_data_done(&sink_input_data
);
723 if (!pa_proplist_contains(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
))
724 pa_proplist_setf(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
, "Loopback from %s",
725 pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
727 if (!pa_proplist_contains(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
))
728 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
, "abstract");
730 if (!pa_proplist_contains(sink_input_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
)
731 && (n
= pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
732 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
, n
);
734 pa_sink_input_new_data_set_sample_spec(&sink_input_data
, &ss
);
735 pa_sink_input_new_data_set_channel_map(&sink_input_data
, &map
);
736 sink_input_data
.flags
= PA_SINK_INPUT_VARIABLE_RATE
| (remix
? 0 : PA_SINK_INPUT_NO_REMIX
);
738 sink_dont_move
= FALSE
;
739 if (pa_modargs_get_value_boolean(ma
, "sink_dont_move", &sink_dont_move
) < 0) {
740 pa_log("sink_dont_move= expects a boolean argument.");
745 sink_input_data
.flags
|= PA_SINK_INPUT_DONT_MOVE
;
747 pa_sink_input_new(&u
->sink_input
, m
->core
, &sink_input_data
);
748 pa_sink_input_new_data_done(&sink_input_data
);
753 u
->sink_input
->parent
.process_msg
= sink_input_process_msg_cb
;
754 u
->sink_input
->pop
= sink_input_pop_cb
;
755 u
->sink_input
->process_rewind
= sink_input_process_rewind_cb
;
756 u
->sink_input
->kill
= sink_input_kill_cb
;
757 u
->sink_input
->attach
= sink_input_attach_cb
;
758 u
->sink_input
->detach
= sink_input_detach_cb
;
759 u
->sink_input
->update_max_rewind
= sink_input_update_max_rewind_cb
;
760 u
->sink_input
->update_max_request
= sink_input_update_max_request_cb
;
761 u
->sink_input
->may_move_to
= sink_input_may_move_to_cb
;
762 u
->sink_input
->moving
= sink_input_moving_cb
;
763 u
->sink_input
->userdata
= u
;
765 pa_sink_input_set_requested_latency(u
->sink_input
, u
->latency
/3);
767 pa_source_output_new_data_init(&source_output_data
);
768 source_output_data
.driver
= __FILE__
;
769 source_output_data
.module
= m
;
770 pa_source_output_new_data_set_source(&source_output_data
, source
, FALSE
);
772 if (pa_modargs_get_proplist(ma
, "source_output_properties", source_output_data
.proplist
, PA_UPDATE_REPLACE
) < 0) {
773 pa_log("Failed to parse the source_output_properties value.");
774 pa_source_output_new_data_done(&source_output_data
);
778 if (!pa_proplist_contains(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
))
779 pa_proplist_setf(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
, "Loopback to %s",
780 pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
782 if (!pa_proplist_contains(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
))
783 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
, "abstract");
785 if (!pa_proplist_contains(source_output_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
)
786 && (n
= pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
787 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
, n
);
789 pa_source_output_new_data_set_sample_spec(&source_output_data
, &ss
);
790 pa_source_output_new_data_set_channel_map(&source_output_data
, &map
);
791 source_output_data
.flags
= (remix
? 0 : PA_SOURCE_OUTPUT_NO_REMIX
);
793 source_dont_move
= FALSE
;
794 if (pa_modargs_get_value_boolean(ma
, "source_dont_move", &source_dont_move
) < 0) {
795 pa_log("source_dont_move= expects a boolean argument.");
799 if (source_dont_move
)
800 source_output_data
.flags
|= PA_SOURCE_OUTPUT_DONT_MOVE
;
802 pa_source_output_new(&u
->source_output
, m
->core
, &source_output_data
);
803 pa_source_output_new_data_done(&source_output_data
);
805 if (!u
->source_output
)
808 u
->source_output
->parent
.process_msg
= source_output_process_msg_cb
;
809 u
->source_output
->push
= source_output_push_cb
;
810 u
->source_output
->process_rewind
= source_output_process_rewind_cb
;
811 u
->source_output
->kill
= source_output_kill_cb
;
812 u
->source_output
->attach
= source_output_attach_cb
;
813 u
->source_output
->detach
= source_output_detach_cb
;
814 u
->source_output
->state_change
= source_output_state_change_cb
;
815 u
->source_output
->may_move_to
= source_output_may_move_to_cb
;
816 u
->source_output
->moving
= source_output_moving_cb
;
817 u
->source_output
->userdata
= u
;
819 pa_source_output_set_requested_latency(u
->source_output
, u
->latency
/3);
821 pa_sink_input_get_silence(u
->sink_input
, &silence
);
822 u
->memblockq
= pa_memblockq_new(
823 "module-loopback memblockq",
825 MEMBLOCKQ_MAXLENGTH
, /* maxlength */
826 MEMBLOCKQ_MAXLENGTH
, /* tlength */
827 &ss
, /* sample_spec */
831 &silence
); /* silence frame */
832 pa_memblock_unref(silence
.memblock
);
834 u
->asyncmsgq
= pa_asyncmsgq_new(0);
836 pa_sink_input_put(u
->sink_input
);
837 pa_source_output_put(u
->source_output
);
839 if (u
->adjust_time
> 0)
840 u
->time_event
= pa_core_rttime_new(m
->core
, pa_rtclock_now() + u
->adjust_time
, time_callback
, u
);
854 void pa__done(pa_module
*m
) {
859 if (!(u
= m
->userdata
))
865 pa_memblockq_free(u
->memblockq
);
868 pa_asyncmsgq_unref(u
->asyncmsgq
);