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
[] = {
116 "source_output_name",
118 "source_output_role",
123 SINK_INPUT_MESSAGE_POST
= PA_SINK_INPUT_MESSAGE_MAX
,
124 SINK_INPUT_MESSAGE_REWIND
,
125 SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
,
126 SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
130 SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
133 /* Called from main context */
134 static void teardown(struct userdata
*u
) {
136 pa_assert_ctl_context();
139 pa_sink_input_unlink(u
->sink_input
);
141 if (u
->source_output
)
142 pa_source_output_unlink(u
->source_output
);
145 pa_sink_input_unref(u
->sink_input
);
146 u
->sink_input
= NULL
;
149 if (u
->source_output
) {
150 pa_source_output_unref(u
->source_output
);
151 u
->source_output
= NULL
;
155 /* Called from main context */
156 static void adjust_rates(struct userdata
*u
) {
158 uint32_t old_rate
, base_rate
, new_rate
;
159 pa_usec_t buffer_latency
;
162 pa_assert_ctl_context();
164 pa_asyncmsgq_send(u
->source_output
->source
->asyncmsgq
, PA_MSGOBJECT(u
->source_output
), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
, NULL
, 0, NULL
);
165 pa_asyncmsgq_send(u
->sink_input
->sink
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
, NULL
, 0, NULL
);
168 u
->latency_snapshot
.sink_input_buffer
+
169 u
->latency_snapshot
.source_output_buffer
;
171 if (u
->latency_snapshot
.recv_counter
<= u
->latency_snapshot
.send_counter
)
172 buffer
+= (size_t) (u
->latency_snapshot
.send_counter
- u
->latency_snapshot
.recv_counter
);
174 buffer
+= PA_CLIP_SUB(buffer
, (size_t) (u
->latency_snapshot
.recv_counter
- u
->latency_snapshot
.send_counter
));
176 buffer_latency
= pa_bytes_to_usec(buffer
, &u
->sink_input
->sample_spec
);
178 pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
179 (double) u
->latency_snapshot
.sink_latency
/ PA_USEC_PER_MSEC
,
180 (double) buffer_latency
/ PA_USEC_PER_MSEC
,
181 (double) u
->latency_snapshot
.source_latency
/ PA_USEC_PER_MSEC
,
182 ((double) u
->latency_snapshot
.sink_latency
+ buffer_latency
+ u
->latency_snapshot
.source_latency
) / PA_USEC_PER_MSEC
);
184 pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes",
185 u
->latency_snapshot
.max_request
*2,
186 u
->latency_snapshot
.min_memblockq_length
);
188 fs
= pa_frame_size(&u
->sink_input
->sample_spec
);
189 old_rate
= u
->sink_input
->sample_spec
.rate
;
190 base_rate
= u
->source_output
->sample_spec
.rate
;
192 if (u
->latency_snapshot
.min_memblockq_length
< u
->latency_snapshot
.max_request
*2)
193 new_rate
= base_rate
- (((u
->latency_snapshot
.max_request
*2 - u
->latency_snapshot
.min_memblockq_length
) / fs
) *PA_USEC_PER_SEC
)/u
->adjust_time
;
195 new_rate
= base_rate
+ (((u
->latency_snapshot
.min_memblockq_length
- u
->latency_snapshot
.max_request
*2) / fs
) *PA_USEC_PER_SEC
)/u
->adjust_time
;
197 if (new_rate
< (uint32_t) (base_rate
*0.8) || new_rate
> (uint32_t) (base_rate
*1.25)) {
198 pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate
, new_rate
);
199 new_rate
= base_rate
;
201 if (base_rate
< new_rate
+ 20 && new_rate
< base_rate
+ 20)
202 new_rate
= base_rate
;
203 /* Do the adjustment in small steps; 2‰ can be considered inaudible */
204 if (new_rate
< (uint32_t) (old_rate
*0.998) || new_rate
> (uint32_t) (old_rate
*1.002)) {
205 pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate
, old_rate
);
206 new_rate
= PA_CLAMP(new_rate
, (uint32_t) (old_rate
*0.998), (uint32_t) (old_rate
*1.002));
210 pa_sink_input_set_rate(u
->sink_input
, new_rate
);
211 pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u
->sink_input
->sink
->name
, (unsigned long) new_rate
);
213 pa_core_rttime_restart(u
->core
, u
->time_event
, pa_rtclock_now() + u
->adjust_time
);
216 /* Called from main context */
217 static void time_callback(pa_mainloop_api
*a
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
218 struct userdata
*u
= userdata
;
222 pa_assert(u
->time_event
== e
);
227 /* Called from input thread context */
228 static void source_output_push_cb(pa_source_output
*o
, const pa_memchunk
*chunk
) {
232 pa_source_output_assert_ref(o
);
233 pa_source_output_assert_io_context(o
);
234 pa_assert_se(u
= o
->userdata
);
236 if (u
->skip
> chunk
->length
) {
237 u
->skip
-= chunk
->length
;
243 copy
.index
+= u
->skip
;
244 copy
.length
-= u
->skip
;
250 pa_asyncmsgq_post(u
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_POST
, NULL
, 0, chunk
, NULL
);
251 u
->send_counter
+= (int64_t) chunk
->length
;
254 /* Called from input thread context */
255 static void source_output_process_rewind_cb(pa_source_output
*o
, size_t nbytes
) {
258 pa_source_output_assert_ref(o
);
259 pa_source_output_assert_io_context(o
);
260 pa_assert_se(u
= o
->userdata
);
262 pa_asyncmsgq_post(u
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_REWIND
, NULL
, (int64_t) nbytes
, NULL
, NULL
);
263 u
->send_counter
-= (int64_t) nbytes
;
266 /* Called from output thread context */
267 static int source_output_process_msg_cb(pa_msgobject
*obj
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
268 struct userdata
*u
= PA_SOURCE_OUTPUT(obj
)->userdata
;
272 case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
: {
275 length
= pa_memblockq_get_length(u
->source_output
->thread_info
.delay_memblockq
);
277 u
->latency_snapshot
.send_counter
= u
->send_counter
;
278 u
->latency_snapshot
.source_output_buffer
= u
->source_output
->thread_info
.resampler
? pa_resampler_result(u
->source_output
->thread_info
.resampler
, length
) : length
;
279 u
->latency_snapshot
.source_latency
= pa_source_get_latency_within_thread(u
->source_output
->source
);
285 return pa_source_output_process_msg(obj
, code
, data
, offset
, chunk
);
288 /* Called from output thread context */
289 static void source_output_attach_cb(pa_source_output
*o
) {
292 pa_source_output_assert_ref(o
);
293 pa_source_output_assert_io_context(o
);
294 pa_assert_se(u
= o
->userdata
);
296 u
->rtpoll_item_write
= pa_rtpoll_item_new_asyncmsgq_write(
297 o
->source
->thread_info
.rtpoll
,
302 /* Called from output thread context */
303 static void source_output_detach_cb(pa_source_output
*o
) {
306 pa_source_output_assert_ref(o
);
307 pa_source_output_assert_io_context(o
);
308 pa_assert_se(u
= o
->userdata
);
310 if (u
->rtpoll_item_write
) {
311 pa_rtpoll_item_free(u
->rtpoll_item_write
);
312 u
->rtpoll_item_write
= NULL
;
316 /* Called from output thread context */
317 static void source_output_state_change_cb(pa_source_output
*o
, pa_source_output_state_t state
) {
320 pa_source_output_assert_ref(o
);
321 pa_source_output_assert_io_context(o
);
322 pa_assert_se(u
= o
->userdata
);
324 if (PA_SOURCE_OUTPUT_IS_LINKED(state
) && o
->thread_info
.state
== PA_SOURCE_OUTPUT_INIT
) {
326 u
->skip
= pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o
->source
),
330 pa_log_info("Skipping %lu bytes", (unsigned long) u
->skip
);
334 /* Called from main thread */
335 static void source_output_kill_cb(pa_source_output
*o
) {
338 pa_source_output_assert_ref(o
);
339 pa_assert_ctl_context();
340 pa_assert_se(u
= o
->userdata
);
343 pa_module_unload_request(u
->module
, TRUE
);
346 /* Called from main thread */
347 static pa_bool_t
source_output_may_move_to_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 return dest
!= u
->sink_input
->sink
->monitor_source
;
357 /* Called from main thread */
358 static void source_output_moving_cb(pa_source_output
*o
, pa_source
*dest
) {
363 pa_source_output_assert_ref(o
);
364 pa_assert_ctl_context();
365 pa_assert_se(u
= o
->userdata
);
367 p
= pa_proplist_new();
368 pa_proplist_setf(p
, PA_PROP_MEDIA_NAME
, "Loopback of %s", pa_strnull(pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
370 if ((n
= pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
371 pa_proplist_sets(p
, PA_PROP_MEDIA_ICON_NAME
, n
);
373 pa_sink_input_update_proplist(u
->sink_input
, PA_UPDATE_REPLACE
, p
);
377 /* Called from output thread context */
378 static void update_min_memblockq_length(struct userdata
*u
) {
382 pa_sink_input_assert_io_context(u
->sink_input
);
384 length
= pa_memblockq_get_length(u
->memblockq
);
386 if (u
->min_memblockq_length
== (size_t) -1 ||
387 length
< u
->min_memblockq_length
)
388 u
->min_memblockq_length
= length
;
391 /* Called from output thread context */
392 static int sink_input_pop_cb(pa_sink_input
*i
, size_t nbytes
, pa_memchunk
*chunk
) {
395 pa_sink_input_assert_ref(i
);
396 pa_sink_input_assert_io_context(i
);
397 pa_assert_se(u
= i
->userdata
);
401 while (pa_asyncmsgq_process_one(u
->asyncmsgq
) > 0)
405 if (pa_memblockq_peek(u
->memblockq
, chunk
) < 0) {
406 pa_log_info("Coud not peek into queue");
410 chunk
->length
= PA_MIN(chunk
->length
, nbytes
);
411 pa_memblockq_drop(u
->memblockq
, chunk
->length
);
413 update_min_memblockq_length(u
);
418 /* Called from output thread context */
419 static void sink_input_process_rewind_cb(pa_sink_input
*i
, size_t nbytes
) {
422 pa_sink_input_assert_ref(i
);
423 pa_sink_input_assert_io_context(i
);
424 pa_assert_se(u
= i
->userdata
);
426 pa_memblockq_rewind(u
->memblockq
, nbytes
);
429 /* Called from output thread context */
430 static int sink_input_process_msg_cb(pa_msgobject
*obj
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
431 struct userdata
*u
= PA_SINK_INPUT(obj
)->userdata
;
435 case PA_SINK_INPUT_MESSAGE_GET_LATENCY
: {
438 pa_sink_input_assert_io_context(u
->sink_input
);
440 *r
= pa_bytes_to_usec(pa_memblockq_get_length(u
->memblockq
), &u
->sink_input
->sample_spec
);
442 /* Fall through, the default handler will add in the extra
443 * latency added by the resampler */
447 case SINK_INPUT_MESSAGE_POST
:
449 pa_sink_input_assert_io_context(u
->sink_input
);
451 if (PA_SINK_IS_OPENED(u
->sink_input
->sink
->thread_info
.state
))
452 pa_memblockq_push_align(u
->memblockq
, chunk
);
454 pa_memblockq_flush_write(u
->memblockq
, TRUE
);
456 update_min_memblockq_length(u
);
458 /* Is this the end of an underrun? Then let's start things
461 u
->sink_input
->thread_info
.underrun_for
> 0 &&
462 pa_memblockq_is_readable(u
->memblockq
)) {
464 pa_log_debug("Requesting rewind due to end of underrun.");
465 pa_sink_input_request_rewind(u
->sink_input
,
466 (size_t) (u
->sink_input
->thread_info
.underrun_for
== (size_t) -1 ? 0 : u
->sink_input
->thread_info
.underrun_for
),
470 u
->recv_counter
+= (int64_t) chunk
->length
;
474 case SINK_INPUT_MESSAGE_REWIND
:
476 pa_sink_input_assert_io_context(u
->sink_input
);
478 if (PA_SINK_IS_OPENED(u
->sink_input
->sink
->thread_info
.state
))
479 pa_memblockq_seek(u
->memblockq
, -offset
, PA_SEEK_RELATIVE
, TRUE
);
481 pa_memblockq_flush_write(u
->memblockq
, TRUE
);
483 u
->recv_counter
-= offset
;
485 update_min_memblockq_length(u
);
489 case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
: {
492 update_min_memblockq_length(u
);
494 length
= pa_memblockq_get_length(u
->sink_input
->thread_info
.render_memblockq
);
496 u
->latency_snapshot
.recv_counter
= u
->recv_counter
;
497 u
->latency_snapshot
.sink_input_buffer
=
498 pa_memblockq_get_length(u
->memblockq
) +
499 (u
->sink_input
->thread_info
.resampler
? pa_resampler_request(u
->sink_input
->thread_info
.resampler
, length
) : length
);
500 u
->latency_snapshot
.sink_latency
= pa_sink_get_latency_within_thread(u
->sink_input
->sink
);
502 u
->latency_snapshot
.max_request
= pa_sink_input_get_max_request(u
->sink_input
);
504 u
->latency_snapshot
.min_memblockq_length
= u
->min_memblockq_length
;
505 u
->min_memblockq_length
= (size_t) -1;
510 case SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
: {
511 /* This message is sent from the IO thread to the main
512 * thread! So don't be confused. All the user cases above
513 * are executed in thread context, but this one is not! */
515 pa_assert_ctl_context();
517 if (u
->adjust_time
> 0)
523 return pa_sink_input_process_msg(obj
, code
, data
, offset
, chunk
);
526 /* Called from output thread context */
527 static void sink_input_attach_cb(pa_sink_input
*i
) {
530 pa_sink_input_assert_ref(i
);
531 pa_sink_input_assert_io_context(i
);
532 pa_assert_se(u
= i
->userdata
);
534 u
->rtpoll_item_read
= pa_rtpoll_item_new_asyncmsgq_read(
535 i
->sink
->thread_info
.rtpoll
,
539 pa_memblockq_set_prebuf(u
->memblockq
, pa_sink_input_get_max_request(i
)*2);
540 pa_memblockq_set_maxrewind(u
->memblockq
, pa_sink_input_get_max_rewind(i
));
542 u
->min_memblockq_length
= (size_t) -1;
545 /* Called from output thread context */
546 static void sink_input_detach_cb(pa_sink_input
*i
) {
549 pa_sink_input_assert_ref(i
);
550 pa_sink_input_assert_io_context(i
);
551 pa_assert_se(u
= i
->userdata
);
553 if (u
->rtpoll_item_read
) {
554 pa_rtpoll_item_free(u
->rtpoll_item_read
);
555 u
->rtpoll_item_read
= NULL
;
559 /* Called from output thread context */
560 static void sink_input_update_max_rewind_cb(pa_sink_input
*i
, size_t nbytes
) {
563 pa_sink_input_assert_ref(i
);
564 pa_sink_input_assert_io_context(i
);
565 pa_assert_se(u
= i
->userdata
);
567 pa_memblockq_set_maxrewind(u
->memblockq
, nbytes
);
570 /* Called from output thread context */
571 static void sink_input_update_max_request_cb(pa_sink_input
*i
, size_t nbytes
) {
574 pa_sink_input_assert_ref(i
);
575 pa_sink_input_assert_io_context(i
);
576 pa_assert_se(u
= i
->userdata
);
578 pa_memblockq_set_prebuf(u
->memblockq
, nbytes
*2);
579 pa_log_info("Max request changed");
580 pa_asyncmsgq_post(pa_thread_mq_get()->outq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
, NULL
, 0, NULL
, NULL
);
583 /* Called from main thread */
584 static void sink_input_kill_cb(pa_sink_input
*i
) {
587 pa_sink_input_assert_ref(i
);
588 pa_assert_ctl_context();
589 pa_assert_se(u
= i
->userdata
);
592 pa_module_unload_request(u
->module
, TRUE
);
595 /* Called from main thread */
596 static void sink_input_moving_cb(pa_sink_input
*i
, pa_sink
*dest
) {
601 pa_sink_input_assert_ref(i
);
602 pa_assert_ctl_context();
603 pa_assert_se(u
= i
->userdata
);
605 p
= pa_proplist_new();
606 pa_proplist_setf(p
, PA_PROP_MEDIA_NAME
, "Loopback to %s", pa_strnull(pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
608 if ((n
= pa_proplist_gets(dest
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
609 pa_proplist_sets(p
, PA_PROP_MEDIA_ICON_NAME
, n
);
611 pa_source_output_update_proplist(u
->source_output
, PA_UPDATE_REPLACE
, p
);
615 /* Called from main thread */
616 static pa_bool_t
sink_input_may_move_to_cb(pa_sink_input
*i
, pa_sink
*dest
) {
619 pa_sink_input_assert_ref(i
);
620 pa_assert_ctl_context();
621 pa_assert_se(u
= i
->userdata
);
623 if (!u
->source_output
->source
->monitor_of
)
626 return dest
!= u
->source_output
->source
->monitor_of
;
629 int pa__init(pa_module
*m
) {
630 pa_modargs
*ma
= NULL
;
633 pa_sink_input_new_data sink_input_data
;
635 pa_source_output_new_data source_output_data
;
636 uint32_t latency_msec
;
640 uint32_t adjust_time_sec
;
645 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
646 pa_log("Failed to parse module arguments");
650 if (!(source
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "source", NULL
), PA_NAMEREG_SOURCE
))) {
651 pa_log("No such source.");
655 if (!(sink
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "sink", NULL
), PA_NAMEREG_SINK
))) {
656 pa_log("No such sink.");
660 ss
= sink
->sample_spec
;
661 map
= sink
->channel_map
;
662 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
663 pa_log("Invalid sample format specification or channel map");
667 latency_msec
= DEFAULT_LATENCY_MSEC
;
668 if (pa_modargs_get_value_u32(ma
, "latency_msec", &latency_msec
) < 0 || latency_msec
< 1 || latency_msec
> 2000) {
669 pa_log("Invalid latency specification");
673 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
676 u
->latency
= (pa_usec_t
) latency_msec
* PA_USEC_PER_MSEC
;
678 adjust_time_sec
= DEFAULT_ADJUST_TIME_USEC
/ PA_USEC_PER_SEC
;
679 if (pa_modargs_get_value_u32(ma
, "adjust_time", &adjust_time_sec
) < 0) {
680 pa_log("Failed to parse adjust_time value");
684 if (adjust_time_sec
!= DEFAULT_ADJUST_TIME_USEC
/ PA_USEC_PER_SEC
)
685 u
->adjust_time
= adjust_time_sec
* PA_USEC_PER_SEC
;
687 u
->adjust_time
= DEFAULT_ADJUST_TIME_USEC
;
689 pa_sink_input_new_data_init(&sink_input_data
);
690 sink_input_data
.driver
= __FILE__
;
691 sink_input_data
.module
= m
;
692 sink_input_data
.sink
= sink
;
694 if ((n
= pa_modargs_get_value(ma
, "sink_input_name", NULL
)))
695 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
, n
);
697 pa_proplist_setf(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
, "Loopback from %s",
698 pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
700 if ((n
= pa_modargs_get_value(ma
, "sink_input_role", NULL
)))
701 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
, n
);
703 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
, "abstract");
705 if ((n
= pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
706 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
, n
);
708 pa_sink_input_new_data_set_sample_spec(&sink_input_data
, &ss
);
709 pa_sink_input_new_data_set_channel_map(&sink_input_data
, &map
);
710 sink_input_data
.flags
= PA_SINK_INPUT_VARIABLE_RATE
;
712 pa_sink_input_new(&u
->sink_input
, m
->core
, &sink_input_data
);
713 pa_sink_input_new_data_done(&sink_input_data
);
718 u
->sink_input
->parent
.process_msg
= sink_input_process_msg_cb
;
719 u
->sink_input
->pop
= sink_input_pop_cb
;
720 u
->sink_input
->process_rewind
= sink_input_process_rewind_cb
;
721 u
->sink_input
->kill
= sink_input_kill_cb
;
722 u
->sink_input
->attach
= sink_input_attach_cb
;
723 u
->sink_input
->detach
= sink_input_detach_cb
;
724 u
->sink_input
->update_max_rewind
= sink_input_update_max_rewind_cb
;
725 u
->sink_input
->update_max_request
= sink_input_update_max_request_cb
;
726 u
->sink_input
->may_move_to
= sink_input_may_move_to_cb
;
727 u
->sink_input
->moving
= sink_input_moving_cb
;
728 u
->sink_input
->userdata
= u
;
730 pa_sink_input_set_requested_latency(u
->sink_input
, u
->latency
/3);
732 pa_source_output_new_data_init(&source_output_data
);
733 source_output_data
.driver
= __FILE__
;
734 source_output_data
.module
= m
;
735 source_output_data
.source
= source
;
737 if ((n
= pa_modargs_get_value(ma
, "source_output_name", NULL
)))
738 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
, n
);
740 pa_proplist_setf(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
, "Loopback to %s",
741 pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
743 if ((n
= pa_modargs_get_value(ma
, "source_output_role", NULL
)))
744 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
, n
);
746 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
, "abstract");
748 if ((n
= pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
749 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
, n
);
751 pa_source_output_new_data_set_sample_spec(&source_output_data
, &ss
);
752 pa_sink_input_new_data_set_channel_map(&sink_input_data
, &map
);
754 pa_source_output_new(&u
->source_output
, m
->core
, &source_output_data
);
755 pa_source_output_new_data_done(&source_output_data
);
757 if (!u
->source_output
)
760 u
->source_output
->parent
.process_msg
= source_output_process_msg_cb
;
761 u
->source_output
->push
= source_output_push_cb
;
762 u
->source_output
->process_rewind
= source_output_process_rewind_cb
;
763 u
->source_output
->kill
= source_output_kill_cb
;
764 u
->source_output
->attach
= source_output_attach_cb
;
765 u
->source_output
->detach
= source_output_detach_cb
;
766 u
->source_output
->state_change
= source_output_state_change_cb
;
767 u
->source_output
->may_move_to
= source_output_may_move_to_cb
;
768 u
->source_output
->moving
= source_output_moving_cb
;
769 u
->source_output
->userdata
= u
;
771 pa_source_output_set_requested_latency(u
->source_output
, u
->latency
/3);
773 pa_sink_input_get_silence(u
->sink_input
, &silence
);
774 u
->memblockq
= pa_memblockq_new(
776 MEMBLOCKQ_MAXLENGTH
, /* maxlength */
777 MEMBLOCKQ_MAXLENGTH
, /* tlength */
778 pa_frame_size(&ss
), /* base */
782 &silence
); /* silence frame */
783 pa_memblock_unref(silence
.memblock
);
785 u
->asyncmsgq
= pa_asyncmsgq_new(0);
787 pa_sink_input_put(u
->sink_input
);
788 pa_source_output_put(u
->source_output
);
790 if (u
->adjust_time
> 0)
791 u
->time_event
= pa_core_rttime_new(m
->core
, pa_rtclock_now() + u
->adjust_time
, time_callback
, u
);
805 void pa__done(pa_module
*m
) {
810 if (!(u
= m
->userdata
))
816 pa_memblockq_free(u
->memblockq
);
819 pa_asyncmsgq_unref(u
->asyncmsgq
);
822 u
->core
->mainloop
->time_free(u
->time_event
);