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> "
61 "source_dont_move=<boolean> "
62 "sink_dont_move=<boolean>");
64 #define DEFAULT_LATENCY_MSEC 200
66 #define MEMBLOCKQ_MAXLENGTH (1024*1024*16)
68 #define DEFAULT_ADJUST_TIME_USEC (10*PA_USEC_PER_SEC)
74 pa_sink_input
*sink_input
;
75 pa_source_output
*source_output
;
77 pa_asyncmsgq
*asyncmsgq
;
78 pa_memblockq
*memblockq
;
80 pa_rtpoll_item
*rtpoll_item_read
, *rtpoll_item_write
;
82 pa_time_event
*time_event
;
83 pa_usec_t adjust_time
;
92 size_t min_memblockq_length
;
96 size_t source_output_buffer
;
97 pa_usec_t source_latency
;
100 size_t sink_input_buffer
;
101 pa_usec_t sink_latency
;
103 size_t min_memblockq_length
;
108 static const char* const valid_modargs
[] = {
118 "source_output_name",
120 "source_output_role",
127 SINK_INPUT_MESSAGE_POST
= PA_SINK_INPUT_MESSAGE_MAX
,
128 SINK_INPUT_MESSAGE_REWIND
,
129 SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
,
130 SINK_INPUT_MESSAGE_MAX_REQUEST_CHANGED
134 SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
137 /* Called from main context */
138 static void teardown(struct userdata
*u
) {
140 pa_assert_ctl_context();
143 pa_sink_input_unlink(u
->sink_input
);
145 if (u
->source_output
)
146 pa_source_output_unlink(u
->source_output
);
149 pa_sink_input_unref(u
->sink_input
);
150 u
->sink_input
= NULL
;
153 if (u
->source_output
) {
154 pa_source_output_unref(u
->source_output
);
155 u
->source_output
= NULL
;
159 /* Called from main context */
160 static void adjust_rates(struct userdata
*u
) {
162 uint32_t old_rate
, base_rate
, new_rate
;
163 pa_usec_t buffer_latency
;
166 pa_assert_ctl_context();
168 pa_asyncmsgq_send(u
->source_output
->source
->asyncmsgq
, PA_MSGOBJECT(u
->source_output
), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
, NULL
, 0, NULL
);
169 pa_asyncmsgq_send(u
->sink_input
->sink
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT
, NULL
, 0, NULL
);
172 u
->latency_snapshot
.sink_input_buffer
+
173 u
->latency_snapshot
.source_output_buffer
;
175 if (u
->latency_snapshot
.recv_counter
<= u
->latency_snapshot
.send_counter
)
176 buffer
+= (size_t) (u
->latency_snapshot
.send_counter
- u
->latency_snapshot
.recv_counter
);
178 buffer
+= PA_CLIP_SUB(buffer
, (size_t) (u
->latency_snapshot
.recv_counter
- u
->latency_snapshot
.send_counter
));
180 buffer_latency
= pa_bytes_to_usec(buffer
, &u
->sink_input
->sample_spec
);
182 pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms",
183 (double) u
->latency_snapshot
.sink_latency
/ PA_USEC_PER_MSEC
,
184 (double) buffer_latency
/ PA_USEC_PER_MSEC
,
185 (double) u
->latency_snapshot
.source_latency
/ PA_USEC_PER_MSEC
,
186 ((double) u
->latency_snapshot
.sink_latency
+ buffer_latency
+ u
->latency_snapshot
.source_latency
) / PA_USEC_PER_MSEC
);
188 pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes",
189 u
->latency_snapshot
.max_request
*2,
190 u
->latency_snapshot
.min_memblockq_length
);
192 fs
= pa_frame_size(&u
->sink_input
->sample_spec
);
193 old_rate
= u
->sink_input
->sample_spec
.rate
;
194 base_rate
= u
->source_output
->sample_spec
.rate
;
196 if (u
->latency_snapshot
.min_memblockq_length
< u
->latency_snapshot
.max_request
*2)
197 new_rate
= base_rate
- (((u
->latency_snapshot
.max_request
*2 - u
->latency_snapshot
.min_memblockq_length
) / fs
) *PA_USEC_PER_SEC
)/u
->adjust_time
;
199 new_rate
= base_rate
+ (((u
->latency_snapshot
.min_memblockq_length
- u
->latency_snapshot
.max_request
*2) / fs
) *PA_USEC_PER_SEC
)/u
->adjust_time
;
201 if (new_rate
< (uint32_t) (base_rate
*0.8) || new_rate
> (uint32_t) (base_rate
*1.25)) {
202 pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate
, new_rate
);
203 new_rate
= base_rate
;
205 if (base_rate
< new_rate
+ 20 && new_rate
< base_rate
+ 20)
206 new_rate
= base_rate
;
207 /* Do the adjustment in small steps; 2‰ can be considered inaudible */
208 if (new_rate
< (uint32_t) (old_rate
*0.998) || new_rate
> (uint32_t) (old_rate
*1.002)) {
209 pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate
, old_rate
);
210 new_rate
= PA_CLAMP(new_rate
, (uint32_t) (old_rate
*0.998), (uint32_t) (old_rate
*1.002));
214 pa_sink_input_set_rate(u
->sink_input
, new_rate
);
215 pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u
->sink_input
->sink
->name
, (unsigned long) new_rate
);
217 pa_core_rttime_restart(u
->core
, u
->time_event
, pa_rtclock_now() + u
->adjust_time
);
220 /* Called from main context */
221 static void time_callback(pa_mainloop_api
*a
, pa_time_event
*e
, const struct timeval
*t
, void *userdata
) {
222 struct userdata
*u
= userdata
;
226 pa_assert(u
->time_event
== e
);
231 /* Called from input thread context */
232 static void source_output_push_cb(pa_source_output
*o
, const pa_memchunk
*chunk
) {
236 pa_source_output_assert_ref(o
);
237 pa_source_output_assert_io_context(o
);
238 pa_assert_se(u
= o
->userdata
);
240 if (u
->skip
> chunk
->length
) {
241 u
->skip
-= chunk
->length
;
247 copy
.index
+= u
->skip
;
248 copy
.length
-= u
->skip
;
254 pa_asyncmsgq_post(u
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_POST
, NULL
, 0, chunk
, NULL
);
255 u
->send_counter
+= (int64_t) chunk
->length
;
258 /* Called from input thread context */
259 static void source_output_process_rewind_cb(pa_source_output
*o
, size_t nbytes
) {
262 pa_source_output_assert_ref(o
);
263 pa_source_output_assert_io_context(o
);
264 pa_assert_se(u
= o
->userdata
);
266 pa_asyncmsgq_post(u
->asyncmsgq
, PA_MSGOBJECT(u
->sink_input
), SINK_INPUT_MESSAGE_REWIND
, NULL
, (int64_t) nbytes
, NULL
, NULL
);
267 u
->send_counter
-= (int64_t) nbytes
;
270 /* Called from output thread context */
271 static int source_output_process_msg_cb(pa_msgobject
*obj
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
272 struct userdata
*u
= PA_SOURCE_OUTPUT(obj
)->userdata
;
276 case SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT
: {
279 length
= pa_memblockq_get_length(u
->source_output
->thread_info
.delay_memblockq
);
281 u
->latency_snapshot
.send_counter
= u
->send_counter
;
282 u
->latency_snapshot
.source_output_buffer
= u
->source_output
->thread_info
.resampler
? pa_resampler_result(u
->source_output
->thread_info
.resampler
, length
) : length
;
283 u
->latency_snapshot
.source_latency
= pa_source_get_latency_within_thread(u
->source_output
->source
);
289 return pa_source_output_process_msg(obj
, code
, data
, offset
, chunk
);
292 /* Called from output thread context */
293 static void source_output_attach_cb(pa_source_output
*o
) {
296 pa_source_output_assert_ref(o
);
297 pa_source_output_assert_io_context(o
);
298 pa_assert_se(u
= o
->userdata
);
300 u
->rtpoll_item_write
= pa_rtpoll_item_new_asyncmsgq_write(
301 o
->source
->thread_info
.rtpoll
,
306 /* Called from output thread context */
307 static void source_output_detach_cb(pa_source_output
*o
) {
310 pa_source_output_assert_ref(o
);
311 pa_source_output_assert_io_context(o
);
312 pa_assert_se(u
= o
->userdata
);
314 if (u
->rtpoll_item_write
) {
315 pa_rtpoll_item_free(u
->rtpoll_item_write
);
316 u
->rtpoll_item_write
= NULL
;
320 /* Called from output thread context */
321 static void source_output_state_change_cb(pa_source_output
*o
, pa_source_output_state_t state
) {
324 pa_source_output_assert_ref(o
);
325 pa_source_output_assert_io_context(o
);
326 pa_assert_se(u
= o
->userdata
);
328 if (PA_SOURCE_OUTPUT_IS_LINKED(state
) && o
->thread_info
.state
== PA_SOURCE_OUTPUT_INIT
) {
330 u
->skip
= pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o
->source
),
334 pa_log_info("Skipping %lu bytes", (unsigned long) u
->skip
);
338 /* Called from main thread */
339 static void source_output_kill_cb(pa_source_output
*o
) {
342 pa_source_output_assert_ref(o
);
343 pa_assert_ctl_context();
344 pa_assert_se(u
= o
->userdata
);
347 pa_module_unload_request(u
->module
, TRUE
);
350 /* Called from main thread */
351 static pa_bool_t
source_output_may_move_to_cb(pa_source_output
*o
, pa_source
*dest
) {
354 pa_source_output_assert_ref(o
);
355 pa_assert_ctl_context();
356 pa_assert_se(u
= o
->userdata
);
358 return dest
!= u
->sink_input
->sink
->monitor_source
;
361 /* Called from main thread */
362 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("Coud 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
;
651 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
652 pa_log("Failed to parse module arguments");
656 if (!(source
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "source", NULL
), PA_NAMEREG_SOURCE
))) {
657 pa_log("No such source.");
661 if (!(sink
= pa_namereg_get(m
->core
, pa_modargs_get_value(ma
, "sink", NULL
), PA_NAMEREG_SINK
))) {
662 pa_log("No such sink.");
666 ss
= sink
->sample_spec
;
667 map
= sink
->channel_map
;
668 if (pa_modargs_get_sample_spec_and_channel_map(ma
, &ss
, &map
, PA_CHANNEL_MAP_DEFAULT
) < 0) {
669 pa_log("Invalid sample format specification or channel map");
673 latency_msec
= DEFAULT_LATENCY_MSEC
;
674 if (pa_modargs_get_value_u32(ma
, "latency_msec", &latency_msec
) < 0 || latency_msec
< 1 || latency_msec
> 2000) {
675 pa_log("Invalid latency specification");
679 m
->userdata
= u
= pa_xnew0(struct userdata
, 1);
682 u
->latency
= (pa_usec_t
) latency_msec
* PA_USEC_PER_MSEC
;
684 adjust_time_sec
= DEFAULT_ADJUST_TIME_USEC
/ PA_USEC_PER_SEC
;
685 if (pa_modargs_get_value_u32(ma
, "adjust_time", &adjust_time_sec
) < 0) {
686 pa_log("Failed to parse adjust_time value");
690 if (adjust_time_sec
!= DEFAULT_ADJUST_TIME_USEC
/ PA_USEC_PER_SEC
)
691 u
->adjust_time
= adjust_time_sec
* PA_USEC_PER_SEC
;
693 u
->adjust_time
= DEFAULT_ADJUST_TIME_USEC
;
695 pa_sink_input_new_data_init(&sink_input_data
);
696 sink_input_data
.driver
= __FILE__
;
697 sink_input_data
.module
= m
;
698 sink_input_data
.sink
= sink
;
700 if ((n
= pa_modargs_get_value(ma
, "sink_input_name", NULL
)))
701 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
, n
);
703 pa_proplist_setf(sink_input_data
.proplist
, PA_PROP_MEDIA_NAME
, "Loopback from %s",
704 pa_strnull(pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
706 if ((n
= pa_modargs_get_value(ma
, "sink_input_role", NULL
)))
707 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
, n
);
709 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ROLE
, "abstract");
711 if ((n
= pa_proplist_gets(source
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
712 pa_proplist_sets(sink_input_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
, n
);
714 pa_sink_input_new_data_set_sample_spec(&sink_input_data
, &ss
);
715 pa_sink_input_new_data_set_channel_map(&sink_input_data
, &map
);
716 sink_input_data
.flags
= PA_SINK_INPUT_VARIABLE_RATE
;
718 sink_dont_move
= FALSE
;
719 if (pa_modargs_get_value_boolean(ma
, "sink_dont_move", &sink_dont_move
) < 0) {
720 pa_log("sink_dont_move= expects a boolean argument.");
725 sink_input_data
.flags
|= PA_SINK_INPUT_DONT_MOVE
;
727 pa_sink_input_new(&u
->sink_input
, m
->core
, &sink_input_data
);
728 pa_sink_input_new_data_done(&sink_input_data
);
733 u
->sink_input
->parent
.process_msg
= sink_input_process_msg_cb
;
734 u
->sink_input
->pop
= sink_input_pop_cb
;
735 u
->sink_input
->process_rewind
= sink_input_process_rewind_cb
;
736 u
->sink_input
->kill
= sink_input_kill_cb
;
737 u
->sink_input
->attach
= sink_input_attach_cb
;
738 u
->sink_input
->detach
= sink_input_detach_cb
;
739 u
->sink_input
->update_max_rewind
= sink_input_update_max_rewind_cb
;
740 u
->sink_input
->update_max_request
= sink_input_update_max_request_cb
;
741 u
->sink_input
->may_move_to
= sink_input_may_move_to_cb
;
742 u
->sink_input
->moving
= sink_input_moving_cb
;
743 u
->sink_input
->userdata
= u
;
745 pa_sink_input_set_requested_latency(u
->sink_input
, u
->latency
/3);
747 pa_source_output_new_data_init(&source_output_data
);
748 source_output_data
.driver
= __FILE__
;
749 source_output_data
.module
= m
;
750 source_output_data
.source
= source
;
752 if ((n
= pa_modargs_get_value(ma
, "source_output_name", NULL
)))
753 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
, n
);
755 pa_proplist_setf(source_output_data
.proplist
, PA_PROP_MEDIA_NAME
, "Loopback to %s",
756 pa_strnull(pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_DESCRIPTION
)));
758 if ((n
= pa_modargs_get_value(ma
, "source_output_role", NULL
)))
759 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
, n
);
761 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ROLE
, "abstract");
763 if ((n
= pa_proplist_gets(sink
->proplist
, PA_PROP_DEVICE_ICON_NAME
)))
764 pa_proplist_sets(source_output_data
.proplist
, PA_PROP_MEDIA_ICON_NAME
, n
);
766 pa_source_output_new_data_set_sample_spec(&source_output_data
, &ss
);
767 pa_sink_input_new_data_set_channel_map(&sink_input_data
, &map
);
768 source_output_data
.flags
= (pa_source_output_flags_t
)0;
770 source_dont_move
= FALSE
;
771 if (pa_modargs_get_value_boolean(ma
, "source_dont_move", &source_dont_move
) < 0) {
772 pa_log("source_dont_move= expects a boolean argument.");
776 if (source_dont_move
)
777 source_output_data
.flags
|= PA_SOURCE_OUTPUT_DONT_MOVE
;
779 pa_source_output_new(&u
->source_output
, m
->core
, &source_output_data
);
780 pa_source_output_new_data_done(&source_output_data
);
782 if (!u
->source_output
)
785 u
->source_output
->parent
.process_msg
= source_output_process_msg_cb
;
786 u
->source_output
->push
= source_output_push_cb
;
787 u
->source_output
->process_rewind
= source_output_process_rewind_cb
;
788 u
->source_output
->kill
= source_output_kill_cb
;
789 u
->source_output
->attach
= source_output_attach_cb
;
790 u
->source_output
->detach
= source_output_detach_cb
;
791 u
->source_output
->state_change
= source_output_state_change_cb
;
792 u
->source_output
->may_move_to
= source_output_may_move_to_cb
;
793 u
->source_output
->moving
= source_output_moving_cb
;
794 u
->source_output
->userdata
= u
;
796 pa_source_output_set_requested_latency(u
->source_output
, u
->latency
/3);
798 pa_sink_input_get_silence(u
->sink_input
, &silence
);
799 u
->memblockq
= pa_memblockq_new(
801 MEMBLOCKQ_MAXLENGTH
, /* maxlength */
802 MEMBLOCKQ_MAXLENGTH
, /* tlength */
803 pa_frame_size(&ss
), /* base */
807 &silence
); /* silence frame */
808 pa_memblock_unref(silence
.memblock
);
810 u
->asyncmsgq
= pa_asyncmsgq_new(0);
812 pa_sink_input_put(u
->sink_input
);
813 pa_source_output_put(u
->source_output
);
815 if (u
->adjust_time
> 0)
816 u
->time_event
= pa_core_rttime_new(m
->core
, pa_rtclock_now() + u
->adjust_time
, time_callback
, u
);
830 void pa__done(pa_module
*m
) {
835 if (!(u
= m
->userdata
))
841 pa_memblockq_free(u
->memblockq
);
844 pa_asyncmsgq_unref(u
->asyncmsgq
);
847 u
->core
->mainloop
->time_free(u
->time_event
);