153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2008 Lennart Poettering 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stdio.h> 2553a5a1b3Sopenharmony_ci#include <errno.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulse/rtclock.h> 2853a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 2953a5a1b3Sopenharmony_ci#include <pulse/util.h> 3053a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 3153a5a1b3Sopenharmony_ci 3253a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3353a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3453a5a1b3Sopenharmony_ci#include <pulsecore/llist.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/memblockq.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/core-rtclock.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 4253a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 4353a5a1b3Sopenharmony_ci#include <pulsecore/thread.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 4853a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother_2.h> 4953a5a1b3Sopenharmony_ci#else 5053a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother.h> 5153a5a1b3Sopenharmony_ci#endif 5253a5a1b3Sopenharmony_ci 5353a5a1b3Sopenharmony_ci#include <pulsecore/strlist.h> 5453a5a1b3Sopenharmony_ci 5553a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering"); 5653a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Combine multiple sinks to one"); 5753a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 5853a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 5953a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 6053a5a1b3Sopenharmony_ci "sink_name=<name for the sink> " 6153a5a1b3Sopenharmony_ci "sink_properties=<properties for the sink> " 6253a5a1b3Sopenharmony_ci "slaves=<slave sinks> " 6353a5a1b3Sopenharmony_ci "adjust_time=<how often to readjust rates in s> " 6453a5a1b3Sopenharmony_ci "resample_method=<method> " 6553a5a1b3Sopenharmony_ci "format=<sample format> " 6653a5a1b3Sopenharmony_ci "rate=<sample rate> " 6753a5a1b3Sopenharmony_ci "channels=<number of channels> " 6853a5a1b3Sopenharmony_ci "channel_map=<channel map>" 6953a5a1b3Sopenharmony_ci "remix=<boolean>"); 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_ci#define DEFAULT_SINK_NAME "combined" 7253a5a1b3Sopenharmony_ci 7353a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (1024*1024*16) 7453a5a1b3Sopenharmony_ci 7553a5a1b3Sopenharmony_ci#define DEFAULT_ADJUST_TIME_USEC (1*PA_USEC_PER_SEC) 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_ci#define BLOCK_USEC (PA_USEC_PER_MSEC * 200) 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 8053a5a1b3Sopenharmony_ci "sink_name", 8153a5a1b3Sopenharmony_ci "sink_properties", 8253a5a1b3Sopenharmony_ci "slaves", 8353a5a1b3Sopenharmony_ci "adjust_time", 8453a5a1b3Sopenharmony_ci "resample_method", 8553a5a1b3Sopenharmony_ci "format", 8653a5a1b3Sopenharmony_ci "rate", 8753a5a1b3Sopenharmony_ci "channels", 8853a5a1b3Sopenharmony_ci "channel_map", 8953a5a1b3Sopenharmony_ci "remix", 9053a5a1b3Sopenharmony_ci NULL 9153a5a1b3Sopenharmony_ci}; 9253a5a1b3Sopenharmony_ci 9353a5a1b3Sopenharmony_cistruct output { 9453a5a1b3Sopenharmony_ci struct userdata *userdata; 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_ci pa_sink *sink; 9753a5a1b3Sopenharmony_ci pa_sink_input *sink_input; 9853a5a1b3Sopenharmony_ci bool ignore_state_change; 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_ci /* This message queue is only for POST messages, i.e. the messages that 10153a5a1b3Sopenharmony_ci * carry audio data from the sink thread to the output thread. The POST 10253a5a1b3Sopenharmony_ci * messages need to be handled in a separate queue, because the queue is 10353a5a1b3Sopenharmony_ci * processed not only in the output thread mainloop, but also inside the 10453a5a1b3Sopenharmony_ci * sink input pop() callback. Processing other messages (such as 10553a5a1b3Sopenharmony_ci * SET_REQUESTED_LATENCY) is not safe inside the pop() callback; at least 10653a5a1b3Sopenharmony_ci * one reason why it's not safe is that messages that generate rewind 10753a5a1b3Sopenharmony_ci * requests (such as SET_REQUESTED_LATENCY) cause crashes when processed 10853a5a1b3Sopenharmony_ci * in the pop() callback. */ 10953a5a1b3Sopenharmony_ci pa_asyncmsgq *audio_inq; 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_ci /* This message queue is for all other messages than POST from the sink 11253a5a1b3Sopenharmony_ci * thread to the output thread (currently "all other messages" means just 11353a5a1b3Sopenharmony_ci * the SET_REQUESTED_LATENCY message). */ 11453a5a1b3Sopenharmony_ci pa_asyncmsgq *control_inq; 11553a5a1b3Sopenharmony_ci 11653a5a1b3Sopenharmony_ci /* Message queue from the output thread to the sink thread. */ 11753a5a1b3Sopenharmony_ci pa_asyncmsgq *outq; 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci pa_rtpoll_item *audio_inq_rtpoll_item_read, *audio_inq_rtpoll_item_write; 12053a5a1b3Sopenharmony_ci pa_rtpoll_item *control_inq_rtpoll_item_read, *control_inq_rtpoll_item_write; 12153a5a1b3Sopenharmony_ci pa_rtpoll_item *outq_rtpoll_item_read, *outq_rtpoll_item_write; 12253a5a1b3Sopenharmony_ci 12353a5a1b3Sopenharmony_ci pa_memblockq *memblockq; 12453a5a1b3Sopenharmony_ci 12553a5a1b3Sopenharmony_ci /* For communication of the stream latencies to the main thread */ 12653a5a1b3Sopenharmony_ci pa_usec_t total_latency; 12753a5a1b3Sopenharmony_ci struct { 12853a5a1b3Sopenharmony_ci pa_usec_t timestamp; 12953a5a1b3Sopenharmony_ci pa_usec_t sink_latency; 13053a5a1b3Sopenharmony_ci size_t output_memblockq_size; 13153a5a1b3Sopenharmony_ci uint64_t receive_counter; 13253a5a1b3Sopenharmony_ci } latency_snapshot; 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci uint64_t receive_counter; 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci /* For communication of the stream parameters to the sink thread */ 13753a5a1b3Sopenharmony_ci pa_atomic_t max_request; 13853a5a1b3Sopenharmony_ci pa_atomic_t max_latency; 13953a5a1b3Sopenharmony_ci pa_atomic_t min_latency; 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci PA_LLIST_FIELDS(struct output); 14253a5a1b3Sopenharmony_ci}; 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_cistruct userdata { 14553a5a1b3Sopenharmony_ci pa_core *core; 14653a5a1b3Sopenharmony_ci pa_module *module; 14753a5a1b3Sopenharmony_ci pa_sink *sink; 14853a5a1b3Sopenharmony_ci 14953a5a1b3Sopenharmony_ci pa_thread *thread; 15053a5a1b3Sopenharmony_ci pa_thread_mq thread_mq; 15153a5a1b3Sopenharmony_ci pa_rtpoll *rtpoll; 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci pa_time_event *time_event; 15453a5a1b3Sopenharmony_ci pa_usec_t adjust_time; 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci bool automatic; 15753a5a1b3Sopenharmony_ci bool auto_desc; 15853a5a1b3Sopenharmony_ci 15953a5a1b3Sopenharmony_ci pa_strlist *unlinked_slaves; 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci pa_hook_slot *sink_put_slot, *sink_unlink_slot, *sink_state_changed_slot; 16253a5a1b3Sopenharmony_ci 16353a5a1b3Sopenharmony_ci pa_resample_method_t resample_method; 16453a5a1b3Sopenharmony_ci 16553a5a1b3Sopenharmony_ci pa_usec_t block_usec; 16653a5a1b3Sopenharmony_ci pa_usec_t default_min_latency; 16753a5a1b3Sopenharmony_ci pa_usec_t default_max_latency; 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ci pa_idxset* outputs; /* managed in main context */ 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_ci bool remix; 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_ci struct { 17453a5a1b3Sopenharmony_ci PA_LLIST_HEAD(struct output, active_outputs); /* managed in IO thread context */ 17553a5a1b3Sopenharmony_ci pa_atomic_t running; /* we cache that value here, so that every thread can query it cheaply */ 17653a5a1b3Sopenharmony_ci pa_usec_t timestamp; 17753a5a1b3Sopenharmony_ci bool in_null_mode; 17853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 17953a5a1b3Sopenharmony_ci pa_smoother_2 *smoother; 18053a5a1b3Sopenharmony_ci#else 18153a5a1b3Sopenharmony_ci pa_smoother *smoother; 18253a5a1b3Sopenharmony_ci#endif 18353a5a1b3Sopenharmony_ci uint64_t counter; 18453a5a1b3Sopenharmony_ci 18553a5a1b3Sopenharmony_ci uint64_t snapshot_counter; 18653a5a1b3Sopenharmony_ci pa_usec_t snapshot_time; 18753a5a1b3Sopenharmony_ci 18853a5a1b3Sopenharmony_ci pa_usec_t render_timestamp; 18953a5a1b3Sopenharmony_ci } thread_info; 19053a5a1b3Sopenharmony_ci}; 19153a5a1b3Sopenharmony_ci 19253a5a1b3Sopenharmony_cistruct sink_snapshot { 19353a5a1b3Sopenharmony_ci pa_usec_t timestamp; 19453a5a1b3Sopenharmony_ci uint64_t send_counter; 19553a5a1b3Sopenharmony_ci}; 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_cienum { 19853a5a1b3Sopenharmony_ci SINK_MESSAGE_ADD_OUTPUT = PA_SINK_MESSAGE_MAX, 19953a5a1b3Sopenharmony_ci SINK_MESSAGE_REMOVE_OUTPUT, 20053a5a1b3Sopenharmony_ci SINK_MESSAGE_NEED, 20153a5a1b3Sopenharmony_ci SINK_MESSAGE_UPDATE_LATENCY, 20253a5a1b3Sopenharmony_ci SINK_MESSAGE_UPDATE_MAX_REQUEST, 20353a5a1b3Sopenharmony_ci SINK_MESSAGE_UPDATE_LATENCY_RANGE, 20453a5a1b3Sopenharmony_ci SINK_MESSAGE_GET_SNAPSHOT 20553a5a1b3Sopenharmony_ci}; 20653a5a1b3Sopenharmony_ci 20753a5a1b3Sopenharmony_cienum { 20853a5a1b3Sopenharmony_ci SINK_INPUT_MESSAGE_POST = PA_SINK_INPUT_MESSAGE_MAX, 20953a5a1b3Sopenharmony_ci SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, 21053a5a1b3Sopenharmony_ci SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT 21153a5a1b3Sopenharmony_ci}; 21253a5a1b3Sopenharmony_ci 21353a5a1b3Sopenharmony_cistatic void output_disable(struct output *o); 21453a5a1b3Sopenharmony_cistatic void output_enable(struct output *o); 21553a5a1b3Sopenharmony_cistatic void output_free(struct output *o); 21653a5a1b3Sopenharmony_cistatic int output_create_sink_input(struct output *o); 21753a5a1b3Sopenharmony_ci 21853a5a1b3Sopenharmony_ci/* rate controller, called from main context 21953a5a1b3Sopenharmony_ci * - maximum deviation from base rate is less than 1% 22053a5a1b3Sopenharmony_ci * - controller step size is limited to 2.01‰ 22153a5a1b3Sopenharmony_ci * - exhibits hunting with USB or Bluetooth devices 22253a5a1b3Sopenharmony_ci */ 22353a5a1b3Sopenharmony_cistatic uint32_t rate_controller( 22453a5a1b3Sopenharmony_ci struct output *o, 22553a5a1b3Sopenharmony_ci uint32_t base_rate, uint32_t old_rate, 22653a5a1b3Sopenharmony_ci int32_t latency_difference_usec) { 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci double new_rate, new_rate_1, new_rate_2; 22953a5a1b3Sopenharmony_ci double min_cycles_1, min_cycles_2; 23053a5a1b3Sopenharmony_ci 23153a5a1b3Sopenharmony_ci /* Calculate next rate that is not more than 2‰ away from the last rate */ 23253a5a1b3Sopenharmony_ci min_cycles_1 = (double)abs(latency_difference_usec) / o->userdata->adjust_time / 0.002 + 1; 23353a5a1b3Sopenharmony_ci new_rate_1 = old_rate + base_rate * (double)latency_difference_usec / min_cycles_1 / o->userdata->adjust_time; 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ci /* Calculate best rate to correct the current latency offset, limit at 23653a5a1b3Sopenharmony_ci * 1% difference from base_rate */ 23753a5a1b3Sopenharmony_ci min_cycles_2 = (double)abs(latency_difference_usec) / o->userdata->adjust_time / 0.01 + 1; 23853a5a1b3Sopenharmony_ci new_rate_2 = (double)base_rate * (1.0 + (double)latency_difference_usec / min_cycles_2 / o->userdata->adjust_time); 23953a5a1b3Sopenharmony_ci 24053a5a1b3Sopenharmony_ci /* Choose the rate that is nearer to base_rate */ 24153a5a1b3Sopenharmony_ci new_rate = new_rate_2; 24253a5a1b3Sopenharmony_ci if (abs(new_rate_1 - base_rate) < abs(new_rate_2 - base_rate)) 24353a5a1b3Sopenharmony_ci new_rate = new_rate_1; 24453a5a1b3Sopenharmony_ci 24553a5a1b3Sopenharmony_ci return (uint32_t)(new_rate + 0.5); 24653a5a1b3Sopenharmony_ci} 24753a5a1b3Sopenharmony_ci 24853a5a1b3Sopenharmony_cistatic void adjust_rates(struct userdata *u) { 24953a5a1b3Sopenharmony_ci struct output *o; 25053a5a1b3Sopenharmony_ci struct sink_snapshot rdata; 25153a5a1b3Sopenharmony_ci pa_usec_t avg_total_latency = 0; 25253a5a1b3Sopenharmony_ci pa_usec_t target_latency = 0; 25353a5a1b3Sopenharmony_ci pa_usec_t max_sink_latency = 0; 25453a5a1b3Sopenharmony_ci pa_usec_t min_total_latency = (pa_usec_t)-1; 25553a5a1b3Sopenharmony_ci uint32_t base_rate; 25653a5a1b3Sopenharmony_ci uint32_t idx; 25753a5a1b3Sopenharmony_ci unsigned n = 0; 25853a5a1b3Sopenharmony_ci pa_usec_t now; 25953a5a1b3Sopenharmony_ci struct output *o_max; 26053a5a1b3Sopenharmony_ci 26153a5a1b3Sopenharmony_ci pa_assert(u); 26253a5a1b3Sopenharmony_ci pa_sink_assert_ref(u->sink); 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci if (pa_idxset_size(u->outputs) <= 0) 26553a5a1b3Sopenharmony_ci return; 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_ci if (u->sink->state != PA_SINK_RUNNING) 26853a5a1b3Sopenharmony_ci return; 26953a5a1b3Sopenharmony_ci 27053a5a1b3Sopenharmony_ci /* Get sink snapshot */ 27153a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_GET_SNAPSHOT, &rdata, 0, NULL); 27253a5a1b3Sopenharmony_ci 27353a5a1b3Sopenharmony_ci /* The sink snapshot time is the time when the last data was rendered. 27453a5a1b3Sopenharmony_ci * Latency is calculated for that point in time. */ 27553a5a1b3Sopenharmony_ci now = rdata.timestamp; 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci /* Sink snapshot is not yet valid. */ 27853a5a1b3Sopenharmony_ci if (!now) 27953a5a1b3Sopenharmony_ci return; 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) { 28253a5a1b3Sopenharmony_ci pa_usec_t snapshot_latency; 28353a5a1b3Sopenharmony_ci int64_t time_difference; 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci if (!o->sink_input || !PA_SINK_IS_OPENED(o->sink->state)) 28653a5a1b3Sopenharmony_ci continue; 28753a5a1b3Sopenharmony_ci 28853a5a1b3Sopenharmony_ci /* The difference may become negative, because it is probable, that the last 28953a5a1b3Sopenharmony_ci * render time was before the sink input snapshot. In this case, the sink 29053a5a1b3Sopenharmony_ci * had some more latency at the render time, so subtracting the value still 29153a5a1b3Sopenharmony_ci * gives the right result. */ 29253a5a1b3Sopenharmony_ci time_difference = (int64_t)now - (int64_t)o->latency_snapshot.timestamp; 29353a5a1b3Sopenharmony_ci 29453a5a1b3Sopenharmony_ci /* Latency at sink snapshot time is sink input snapshot latency minus time 29553a5a1b3Sopenharmony_ci * passed between the two snapshots. */ 29653a5a1b3Sopenharmony_ci snapshot_latency = o->latency_snapshot.sink_latency 29753a5a1b3Sopenharmony_ci + pa_bytes_to_usec(o->latency_snapshot.output_memblockq_size, &o->sink_input->sample_spec) 29853a5a1b3Sopenharmony_ci - time_difference; 29953a5a1b3Sopenharmony_ci 30053a5a1b3Sopenharmony_ci /* Add the data that was sent between taking the sink input snapshot 30153a5a1b3Sopenharmony_ci * and the sink snapshot. */ 30253a5a1b3Sopenharmony_ci snapshot_latency += pa_bytes_to_usec(rdata.send_counter - o->latency_snapshot.receive_counter, &o->sink_input->sample_spec); 30353a5a1b3Sopenharmony_ci 30453a5a1b3Sopenharmony_ci /* This is the current combined latency of the slave sink and the related 30553a5a1b3Sopenharmony_ci * memblockq at the time of the sink snapshot. */ 30653a5a1b3Sopenharmony_ci o->total_latency = snapshot_latency; 30753a5a1b3Sopenharmony_ci avg_total_latency += snapshot_latency; 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_ci /* Get max_sink_latency and min_total_latency for target selection. */ 31053a5a1b3Sopenharmony_ci if (min_total_latency == (pa_usec_t)-1 || o->total_latency < min_total_latency) 31153a5a1b3Sopenharmony_ci min_total_latency = o->total_latency; 31253a5a1b3Sopenharmony_ci 31353a5a1b3Sopenharmony_ci if (o->latency_snapshot.sink_latency > max_sink_latency) { 31453a5a1b3Sopenharmony_ci max_sink_latency = o->latency_snapshot.sink_latency; 31553a5a1b3Sopenharmony_ci o_max = o; 31653a5a1b3Sopenharmony_ci } 31753a5a1b3Sopenharmony_ci 31853a5a1b3Sopenharmony_ci /* Debug output */ 31953a5a1b3Sopenharmony_ci pa_log_debug("[%s] Snapshot sink latency = %0.2fms, total snapshot latency = %0.2fms", o->sink->name, (double) o->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) snapshot_latency / PA_USEC_PER_MSEC); 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci if (o->total_latency > 10*PA_USEC_PER_SEC) 32253a5a1b3Sopenharmony_ci pa_log_warn("[%s] Total latency of output is very high (%0.2fms), most likely the audio timing in one of your drivers is broken.", o->sink->name, (double) o->total_latency / PA_USEC_PER_MSEC); 32353a5a1b3Sopenharmony_ci 32453a5a1b3Sopenharmony_ci n++; 32553a5a1b3Sopenharmony_ci } 32653a5a1b3Sopenharmony_ci 32753a5a1b3Sopenharmony_ci /* If there is no valid output there is nothing to do. */ 32853a5a1b3Sopenharmony_ci if (min_total_latency == (pa_usec_t) -1) 32953a5a1b3Sopenharmony_ci return; 33053a5a1b3Sopenharmony_ci 33153a5a1b3Sopenharmony_ci avg_total_latency /= n; 33253a5a1b3Sopenharmony_ci 33353a5a1b3Sopenharmony_ci /* The target selection ensures, that at least one of the 33453a5a1b3Sopenharmony_ci * sinks will use the base rate and all other sinks are set 33553a5a1b3Sopenharmony_ci * relative to it. */ 33653a5a1b3Sopenharmony_ci if (max_sink_latency > min_total_latency) 33753a5a1b3Sopenharmony_ci target_latency = o_max->total_latency; 33853a5a1b3Sopenharmony_ci else 33953a5a1b3Sopenharmony_ci target_latency = min_total_latency; 34053a5a1b3Sopenharmony_ci 34153a5a1b3Sopenharmony_ci pa_log_info("[%s] avg total latency is %0.2f msec.", u->sink->name, (double) avg_total_latency / PA_USEC_PER_MSEC); 34253a5a1b3Sopenharmony_ci pa_log_info("[%s] target latency for all slaves is %0.2f msec.", u->sink->name, (double) target_latency / PA_USEC_PER_MSEC); 34353a5a1b3Sopenharmony_ci 34453a5a1b3Sopenharmony_ci base_rate = u->sink->sample_spec.rate; 34553a5a1b3Sopenharmony_ci 34653a5a1b3Sopenharmony_ci /* Calculate and set rates for the sink inputs. */ 34753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) { 34853a5a1b3Sopenharmony_ci uint32_t new_rate; 34953a5a1b3Sopenharmony_ci int32_t latency_difference; 35053a5a1b3Sopenharmony_ci 35153a5a1b3Sopenharmony_ci if (!o->sink_input || !PA_SINK_IS_OPENED(o->sink->state)) 35253a5a1b3Sopenharmony_ci continue; 35353a5a1b3Sopenharmony_ci 35453a5a1b3Sopenharmony_ci latency_difference = (int64_t)o->total_latency - (int64_t)target_latency; 35553a5a1b3Sopenharmony_ci new_rate = rate_controller(o, base_rate, o->sink_input->sample_spec.rate, latency_difference); 35653a5a1b3Sopenharmony_ci 35753a5a1b3Sopenharmony_ci pa_log_info("[%s] new rate is %u Hz; ratio is %0.3f.", o->sink_input->sink->name, new_rate, (double) new_rate / base_rate); 35853a5a1b3Sopenharmony_ci pa_sink_input_set_rate(o->sink_input, new_rate); 35953a5a1b3Sopenharmony_ci } 36053a5a1b3Sopenharmony_ci 36153a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, NULL, (int64_t) avg_total_latency, NULL); 36253a5a1b3Sopenharmony_ci} 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_cistatic void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { 36553a5a1b3Sopenharmony_ci struct userdata *u = userdata; 36653a5a1b3Sopenharmony_ci 36753a5a1b3Sopenharmony_ci pa_assert(u); 36853a5a1b3Sopenharmony_ci pa_assert(a); 36953a5a1b3Sopenharmony_ci pa_assert(u->time_event == e); 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_ci if (u->sink->state == PA_SINK_SUSPENDED) { 37253a5a1b3Sopenharmony_ci u->core->mainloop->time_free(e); 37353a5a1b3Sopenharmony_ci u->time_event = NULL; 37453a5a1b3Sopenharmony_ci } else { 37553a5a1b3Sopenharmony_ci struct output *o; 37653a5a1b3Sopenharmony_ci uint32_t idx; 37753a5a1b3Sopenharmony_ci 37853a5a1b3Sopenharmony_ci pa_core_rttime_restart(u->core, e, pa_rtclock_now() + u->adjust_time); 37953a5a1b3Sopenharmony_ci 38053a5a1b3Sopenharmony_ci /* Get latency snapshots */ 38153a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) { 38253a5a1b3Sopenharmony_ci pa_asyncmsgq_send(o->control_inq, PA_MSGOBJECT(o->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); 38353a5a1b3Sopenharmony_ci } 38453a5a1b3Sopenharmony_ci 38553a5a1b3Sopenharmony_ci } 38653a5a1b3Sopenharmony_ci adjust_rates(u); 38753a5a1b3Sopenharmony_ci} 38853a5a1b3Sopenharmony_ci 38953a5a1b3Sopenharmony_cistatic void process_render_null(struct userdata *u, pa_usec_t now) { 39053a5a1b3Sopenharmony_ci size_t ate = 0; 39153a5a1b3Sopenharmony_ci 39253a5a1b3Sopenharmony_ci pa_assert(u); 39353a5a1b3Sopenharmony_ci pa_assert(u->sink->thread_info.state == PA_SINK_RUNNING); 39453a5a1b3Sopenharmony_ci 39553a5a1b3Sopenharmony_ci if (u->thread_info.in_null_mode) 39653a5a1b3Sopenharmony_ci u->thread_info.timestamp = now; 39753a5a1b3Sopenharmony_ci 39853a5a1b3Sopenharmony_ci while (u->thread_info.timestamp < now + u->block_usec) { 39953a5a1b3Sopenharmony_ci pa_memchunk chunk; 40053a5a1b3Sopenharmony_ci 40153a5a1b3Sopenharmony_ci pa_sink_render(u->sink, u->sink->thread_info.max_request, &chunk); 40253a5a1b3Sopenharmony_ci pa_memblock_unref(chunk.memblock); 40353a5a1b3Sopenharmony_ci 40453a5a1b3Sopenharmony_ci u->thread_info.counter += chunk.length; 40553a5a1b3Sopenharmony_ci 40653a5a1b3Sopenharmony_ci/* pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */ 40753a5a1b3Sopenharmony_ci u->thread_info.timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec); 40853a5a1b3Sopenharmony_ci 40953a5a1b3Sopenharmony_ci ate += chunk.length; 41053a5a1b3Sopenharmony_ci 41153a5a1b3Sopenharmony_ci if (ate >= u->sink->thread_info.max_request) 41253a5a1b3Sopenharmony_ci break; 41353a5a1b3Sopenharmony_ci } 41453a5a1b3Sopenharmony_ci 41553a5a1b3Sopenharmony_ci/* pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */ 41653a5a1b3Sopenharmony_ci 41753a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 41853a5a1b3Sopenharmony_ci pa_smoother_2_put(u->thread_info.smoother, now, 41953a5a1b3Sopenharmony_ci u->thread_info.counter - pa_usec_to_bytes(u->thread_info.timestamp - now, &u->sink->sample_spec)); 42053a5a1b3Sopenharmony_ci#else 42153a5a1b3Sopenharmony_ci pa_smoother_put(u->thread_info.smoother, now, 42253a5a1b3Sopenharmony_ci pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec) - (u->thread_info.timestamp - now)); 42353a5a1b3Sopenharmony_ci#endif 42453a5a1b3Sopenharmony_ci} 42553a5a1b3Sopenharmony_ci 42653a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) { 42753a5a1b3Sopenharmony_ci struct userdata *u = userdata; 42853a5a1b3Sopenharmony_ci 42953a5a1b3Sopenharmony_ci pa_assert(u); 43053a5a1b3Sopenharmony_ci 43153a5a1b3Sopenharmony_ci pa_log_debug("Thread starting up"); 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci if (u->core->realtime_scheduling) 43453a5a1b3Sopenharmony_ci pa_thread_make_realtime(u->core->realtime_priority+1); 43553a5a1b3Sopenharmony_ci 43653a5a1b3Sopenharmony_ci pa_thread_mq_install(&u->thread_mq); 43753a5a1b3Sopenharmony_ci 43853a5a1b3Sopenharmony_ci u->thread_info.timestamp = pa_rtclock_now(); 43953a5a1b3Sopenharmony_ci u->thread_info.in_null_mode = false; 44053a5a1b3Sopenharmony_ci 44153a5a1b3Sopenharmony_ci for (;;) { 44253a5a1b3Sopenharmony_ci int ret; 44353a5a1b3Sopenharmony_ci 44453a5a1b3Sopenharmony_ci if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) 44553a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 44653a5a1b3Sopenharmony_ci 44753a5a1b3Sopenharmony_ci /* If no outputs are connected, render some data and drop it immediately. */ 44853a5a1b3Sopenharmony_ci if (u->sink->thread_info.state == PA_SINK_RUNNING && !u->thread_info.active_outputs) { 44953a5a1b3Sopenharmony_ci pa_usec_t now; 45053a5a1b3Sopenharmony_ci 45153a5a1b3Sopenharmony_ci now = pa_rtclock_now(); 45253a5a1b3Sopenharmony_ci 45353a5a1b3Sopenharmony_ci if (!u->thread_info.in_null_mode || u->thread_info.timestamp <= now) 45453a5a1b3Sopenharmony_ci process_render_null(u, now); 45553a5a1b3Sopenharmony_ci 45653a5a1b3Sopenharmony_ci pa_rtpoll_set_timer_absolute(u->rtpoll, u->thread_info.timestamp); 45753a5a1b3Sopenharmony_ci u->thread_info.in_null_mode = true; 45853a5a1b3Sopenharmony_ci } else { 45953a5a1b3Sopenharmony_ci pa_rtpoll_set_timer_disabled(u->rtpoll); 46053a5a1b3Sopenharmony_ci u->thread_info.in_null_mode = false; 46153a5a1b3Sopenharmony_ci } 46253a5a1b3Sopenharmony_ci 46353a5a1b3Sopenharmony_ci /* Hmm, nothing to do. Let's sleep */ 46453a5a1b3Sopenharmony_ci if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) { 46553a5a1b3Sopenharmony_ci pa_log_info("pa_rtpoll_run() = %i", ret); 46653a5a1b3Sopenharmony_ci goto fail; 46753a5a1b3Sopenharmony_ci } 46853a5a1b3Sopenharmony_ci 46953a5a1b3Sopenharmony_ci if (ret == 0) 47053a5a1b3Sopenharmony_ci goto finish; 47153a5a1b3Sopenharmony_ci } 47253a5a1b3Sopenharmony_ci 47353a5a1b3Sopenharmony_cifail: 47453a5a1b3Sopenharmony_ci /* If this was no regular exit from the loop we have to continue 47553a5a1b3Sopenharmony_ci * processing messages until we received PA_MESSAGE_SHUTDOWN */ 47653a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); 47753a5a1b3Sopenharmony_ci pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); 47853a5a1b3Sopenharmony_ci 47953a5a1b3Sopenharmony_cifinish: 48053a5a1b3Sopenharmony_ci pa_log_debug("Thread shutting down"); 48153a5a1b3Sopenharmony_ci} 48253a5a1b3Sopenharmony_ci 48353a5a1b3Sopenharmony_ci/* Called from combine sink I/O thread context */ 48453a5a1b3Sopenharmony_cistatic void render_memblock(struct userdata *u, struct output *o, size_t length) { 48553a5a1b3Sopenharmony_ci pa_assert(u); 48653a5a1b3Sopenharmony_ci pa_assert(o); 48753a5a1b3Sopenharmony_ci 48853a5a1b3Sopenharmony_ci /* We are run by the sink thread, on behalf of an output (o). The 48953a5a1b3Sopenharmony_ci * output is waiting for us, hence it is safe to access its 49053a5a1b3Sopenharmony_ci * mainblockq and asyncmsgq directly. */ 49153a5a1b3Sopenharmony_ci 49253a5a1b3Sopenharmony_ci /* If we are not running, we cannot produce any data */ 49353a5a1b3Sopenharmony_ci if (!pa_atomic_load(&u->thread_info.running)) 49453a5a1b3Sopenharmony_ci return; 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci /* Maybe there's some data in the requesting output's queue 49753a5a1b3Sopenharmony_ci * now? */ 49853a5a1b3Sopenharmony_ci while (pa_asyncmsgq_process_one(o->audio_inq) > 0) 49953a5a1b3Sopenharmony_ci ; 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci /* Ok, now let's prepare some data if we really have to. Save the 50253a5a1b3Sopenharmony_ci * the time for latency calculations. */ 50353a5a1b3Sopenharmony_ci u->thread_info.render_timestamp = pa_rtclock_now(); 50453a5a1b3Sopenharmony_ci 50553a5a1b3Sopenharmony_ci while (!pa_memblockq_is_readable(o->memblockq)) { 50653a5a1b3Sopenharmony_ci struct output *j; 50753a5a1b3Sopenharmony_ci pa_memchunk chunk; 50853a5a1b3Sopenharmony_ci 50953a5a1b3Sopenharmony_ci /* Render data! */ 51053a5a1b3Sopenharmony_ci pa_sink_render(u->sink, length, &chunk); 51153a5a1b3Sopenharmony_ci 51253a5a1b3Sopenharmony_ci u->thread_info.counter += chunk.length; 51353a5a1b3Sopenharmony_ci o->receive_counter += chunk.length; 51453a5a1b3Sopenharmony_ci 51553a5a1b3Sopenharmony_ci /* OK, let's send this data to the other threads */ 51653a5a1b3Sopenharmony_ci PA_LLIST_FOREACH(j, u->thread_info.active_outputs) { 51753a5a1b3Sopenharmony_ci if (j == o) 51853a5a1b3Sopenharmony_ci continue; 51953a5a1b3Sopenharmony_ci 52053a5a1b3Sopenharmony_ci pa_asyncmsgq_post(j->audio_inq, PA_MSGOBJECT(j->sink_input), SINK_INPUT_MESSAGE_POST, NULL, 0, &chunk, NULL); 52153a5a1b3Sopenharmony_ci } 52253a5a1b3Sopenharmony_ci 52353a5a1b3Sopenharmony_ci /* And place it directly into the requesting output's queue */ 52453a5a1b3Sopenharmony_ci pa_memblockq_push_align(o->memblockq, &chunk); 52553a5a1b3Sopenharmony_ci pa_memblock_unref(chunk.memblock); 52653a5a1b3Sopenharmony_ci } 52753a5a1b3Sopenharmony_ci} 52853a5a1b3Sopenharmony_ci 52953a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 53053a5a1b3Sopenharmony_cistatic void request_memblock(struct output *o, size_t length) { 53153a5a1b3Sopenharmony_ci pa_assert(o); 53253a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(o->sink_input); 53353a5a1b3Sopenharmony_ci pa_sink_assert_ref(o->userdata->sink); 53453a5a1b3Sopenharmony_ci 53553a5a1b3Sopenharmony_ci /* If another thread already prepared some data we received 53653a5a1b3Sopenharmony_ci * the data over the asyncmsgq, hence let's first process 53753a5a1b3Sopenharmony_ci * it. */ 53853a5a1b3Sopenharmony_ci while (pa_asyncmsgq_process_one(o->audio_inq) > 0) 53953a5a1b3Sopenharmony_ci ; 54053a5a1b3Sopenharmony_ci 54153a5a1b3Sopenharmony_ci /* Check whether we're now readable */ 54253a5a1b3Sopenharmony_ci if (pa_memblockq_is_readable(o->memblockq)) 54353a5a1b3Sopenharmony_ci return; 54453a5a1b3Sopenharmony_ci 54553a5a1b3Sopenharmony_ci /* OK, we need to prepare new data, but only if the sink is actually running */ 54653a5a1b3Sopenharmony_ci if (pa_atomic_load(&o->userdata->thread_info.running)) 54753a5a1b3Sopenharmony_ci pa_asyncmsgq_send(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_NEED, o, (int64_t) length, NULL); 54853a5a1b3Sopenharmony_ci} 54953a5a1b3Sopenharmony_ci 55053a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 55153a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { 55253a5a1b3Sopenharmony_ci struct output *o; 55353a5a1b3Sopenharmony_ci 55453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 55553a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 55653a5a1b3Sopenharmony_ci 55753a5a1b3Sopenharmony_ci /* If necessary, get some new data */ 55853a5a1b3Sopenharmony_ci request_memblock(o, nbytes); 55953a5a1b3Sopenharmony_ci 56053a5a1b3Sopenharmony_ci /* pa_log("%s q size is %u + %u (%u/%u)", */ 56153a5a1b3Sopenharmony_ci /* i->sink->name, */ 56253a5a1b3Sopenharmony_ci /* pa_memblockq_get_nblocks(o->memblockq), */ 56353a5a1b3Sopenharmony_ci /* pa_memblockq_get_nblocks(i->thread_info.render_memblockq), */ 56453a5a1b3Sopenharmony_ci /* pa_memblockq_get_maxrewind(o->memblockq), */ 56553a5a1b3Sopenharmony_ci /* pa_memblockq_get_maxrewind(i->thread_info.render_memblockq)); */ 56653a5a1b3Sopenharmony_ci 56753a5a1b3Sopenharmony_ci if (pa_memblockq_peek(o->memblockq, chunk) < 0) 56853a5a1b3Sopenharmony_ci return -1; 56953a5a1b3Sopenharmony_ci 57053a5a1b3Sopenharmony_ci pa_memblockq_drop(o->memblockq, chunk->length); 57153a5a1b3Sopenharmony_ci 57253a5a1b3Sopenharmony_ci return 0; 57353a5a1b3Sopenharmony_ci} 57453a5a1b3Sopenharmony_ci 57553a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 57653a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 57753a5a1b3Sopenharmony_ci struct output *o; 57853a5a1b3Sopenharmony_ci 57953a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 58053a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 58153a5a1b3Sopenharmony_ci 58253a5a1b3Sopenharmony_ci pa_memblockq_rewind(o->memblockq, nbytes); 58353a5a1b3Sopenharmony_ci} 58453a5a1b3Sopenharmony_ci 58553a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 58653a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { 58753a5a1b3Sopenharmony_ci struct output *o; 58853a5a1b3Sopenharmony_ci 58953a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 59053a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 59153a5a1b3Sopenharmony_ci 59253a5a1b3Sopenharmony_ci pa_memblockq_set_maxrewind(o->memblockq, nbytes); 59353a5a1b3Sopenharmony_ci} 59453a5a1b3Sopenharmony_ci 59553a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 59653a5a1b3Sopenharmony_cistatic void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { 59753a5a1b3Sopenharmony_ci struct output *o; 59853a5a1b3Sopenharmony_ci 59953a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 60053a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 60153a5a1b3Sopenharmony_ci 60253a5a1b3Sopenharmony_ci if (pa_atomic_load(&o->max_request) == (int) nbytes) 60353a5a1b3Sopenharmony_ci return; 60453a5a1b3Sopenharmony_ci 60553a5a1b3Sopenharmony_ci pa_atomic_store(&o->max_request, (int) nbytes); 60653a5a1b3Sopenharmony_ci pa_log_debug("Sink input update max request %lu", (unsigned long) nbytes); 60753a5a1b3Sopenharmony_ci pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_MAX_REQUEST, NULL, 0, NULL, NULL); 60853a5a1b3Sopenharmony_ci} 60953a5a1b3Sopenharmony_ci 61053a5a1b3Sopenharmony_ci/* Called from thread context */ 61153a5a1b3Sopenharmony_cistatic void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { 61253a5a1b3Sopenharmony_ci struct output *o; 61353a5a1b3Sopenharmony_ci pa_usec_t min, max, fix; 61453a5a1b3Sopenharmony_ci 61553a5a1b3Sopenharmony_ci pa_assert(i); 61653a5a1b3Sopenharmony_ci 61753a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 61853a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 61953a5a1b3Sopenharmony_ci 62053a5a1b3Sopenharmony_ci fix = i->sink->thread_info.fixed_latency; 62153a5a1b3Sopenharmony_ci if (fix > 0) { 62253a5a1b3Sopenharmony_ci min = fix; 62353a5a1b3Sopenharmony_ci max = fix; 62453a5a1b3Sopenharmony_ci } else { 62553a5a1b3Sopenharmony_ci min = i->sink->thread_info.min_latency; 62653a5a1b3Sopenharmony_ci max = i->sink->thread_info.max_latency; 62753a5a1b3Sopenharmony_ci } 62853a5a1b3Sopenharmony_ci 62953a5a1b3Sopenharmony_ci if ((pa_atomic_load(&o->min_latency) == (int) min) && 63053a5a1b3Sopenharmony_ci (pa_atomic_load(&o->max_latency) == (int) max)) 63153a5a1b3Sopenharmony_ci return; 63253a5a1b3Sopenharmony_ci 63353a5a1b3Sopenharmony_ci pa_atomic_store(&o->min_latency, (int) min); 63453a5a1b3Sopenharmony_ci pa_atomic_store(&o->max_latency, (int) max); 63553a5a1b3Sopenharmony_ci pa_log_debug("Sink input update latency range %lu %lu", (unsigned long) min, (unsigned long) max); 63653a5a1b3Sopenharmony_ci pa_asyncmsgq_post(o->outq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_UPDATE_LATENCY_RANGE, NULL, 0, NULL, NULL); 63753a5a1b3Sopenharmony_ci} 63853a5a1b3Sopenharmony_ci 63953a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 64053a5a1b3Sopenharmony_cistatic void sink_input_attach_cb(pa_sink_input *i) { 64153a5a1b3Sopenharmony_ci struct output *o; 64253a5a1b3Sopenharmony_ci pa_usec_t fix, min, max; 64353a5a1b3Sopenharmony_ci size_t nbytes; 64453a5a1b3Sopenharmony_ci 64553a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 64653a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 64753a5a1b3Sopenharmony_ci 64853a5a1b3Sopenharmony_ci /* Set up the queue from the sink thread to us */ 64953a5a1b3Sopenharmony_ci pa_assert(!o->audio_inq_rtpoll_item_read); 65053a5a1b3Sopenharmony_ci pa_assert(!o->control_inq_rtpoll_item_read); 65153a5a1b3Sopenharmony_ci pa_assert(!o->outq_rtpoll_item_write); 65253a5a1b3Sopenharmony_ci 65353a5a1b3Sopenharmony_ci o->audio_inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( 65453a5a1b3Sopenharmony_ci i->sink->thread_info.rtpoll, 65553a5a1b3Sopenharmony_ci PA_RTPOLL_LATE, /* This one is not that important, since we check for data in _peek() anyway. */ 65653a5a1b3Sopenharmony_ci o->audio_inq); 65753a5a1b3Sopenharmony_ci 65853a5a1b3Sopenharmony_ci o->control_inq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( 65953a5a1b3Sopenharmony_ci i->sink->thread_info.rtpoll, 66053a5a1b3Sopenharmony_ci PA_RTPOLL_NORMAL, 66153a5a1b3Sopenharmony_ci o->control_inq); 66253a5a1b3Sopenharmony_ci 66353a5a1b3Sopenharmony_ci o->outq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( 66453a5a1b3Sopenharmony_ci i->sink->thread_info.rtpoll, 66553a5a1b3Sopenharmony_ci PA_RTPOLL_EARLY, 66653a5a1b3Sopenharmony_ci o->outq); 66753a5a1b3Sopenharmony_ci 66853a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(i, 0, false, true, true); 66953a5a1b3Sopenharmony_ci 67053a5a1b3Sopenharmony_ci nbytes = pa_sink_input_get_max_request(i); 67153a5a1b3Sopenharmony_ci pa_atomic_store(&o->max_request, (int) nbytes); 67253a5a1b3Sopenharmony_ci pa_log_debug("attach max request %lu", (unsigned long) nbytes); 67353a5a1b3Sopenharmony_ci 67453a5a1b3Sopenharmony_ci fix = i->sink->thread_info.fixed_latency; 67553a5a1b3Sopenharmony_ci if (fix > 0) { 67653a5a1b3Sopenharmony_ci min = max = fix; 67753a5a1b3Sopenharmony_ci } else { 67853a5a1b3Sopenharmony_ci min = i->sink->thread_info.min_latency; 67953a5a1b3Sopenharmony_ci max = i->sink->thread_info.max_latency; 68053a5a1b3Sopenharmony_ci } 68153a5a1b3Sopenharmony_ci pa_atomic_store(&o->min_latency, (int) min); 68253a5a1b3Sopenharmony_ci pa_atomic_store(&o->max_latency, (int) max); 68353a5a1b3Sopenharmony_ci pa_log_debug("attach latency range %lu %lu", (unsigned long) min, (unsigned long) max); 68453a5a1b3Sopenharmony_ci 68553a5a1b3Sopenharmony_ci /* We register the output. That means that the sink will start to pass data to 68653a5a1b3Sopenharmony_ci * this output. */ 68753a5a1b3Sopenharmony_ci pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_ADD_OUTPUT, o, 0, NULL); 68853a5a1b3Sopenharmony_ci} 68953a5a1b3Sopenharmony_ci 69053a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 69153a5a1b3Sopenharmony_cistatic void sink_input_detach_cb(pa_sink_input *i) { 69253a5a1b3Sopenharmony_ci struct output *o; 69353a5a1b3Sopenharmony_ci 69453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 69553a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 69653a5a1b3Sopenharmony_ci 69753a5a1b3Sopenharmony_ci /* We unregister the output. That means that the sink doesn't 69853a5a1b3Sopenharmony_ci * pass any further data to this output */ 69953a5a1b3Sopenharmony_ci pa_asyncmsgq_send(o->userdata->sink->asyncmsgq, PA_MSGOBJECT(o->userdata->sink), SINK_MESSAGE_REMOVE_OUTPUT, o, 0, NULL); 70053a5a1b3Sopenharmony_ci 70153a5a1b3Sopenharmony_ci if (o->audio_inq_rtpoll_item_read) { 70253a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->audio_inq_rtpoll_item_read); 70353a5a1b3Sopenharmony_ci o->audio_inq_rtpoll_item_read = NULL; 70453a5a1b3Sopenharmony_ci } 70553a5a1b3Sopenharmony_ci 70653a5a1b3Sopenharmony_ci if (o->control_inq_rtpoll_item_read) { 70753a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->control_inq_rtpoll_item_read); 70853a5a1b3Sopenharmony_ci o->control_inq_rtpoll_item_read = NULL; 70953a5a1b3Sopenharmony_ci } 71053a5a1b3Sopenharmony_ci 71153a5a1b3Sopenharmony_ci if (o->outq_rtpoll_item_write) { 71253a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->outq_rtpoll_item_write); 71353a5a1b3Sopenharmony_ci o->outq_rtpoll_item_write = NULL; 71453a5a1b3Sopenharmony_ci } 71553a5a1b3Sopenharmony_ci 71653a5a1b3Sopenharmony_ci} 71753a5a1b3Sopenharmony_ci 71853a5a1b3Sopenharmony_ci/* Called from main context */ 71953a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) { 72053a5a1b3Sopenharmony_ci struct output *o; 72153a5a1b3Sopenharmony_ci 72253a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 72353a5a1b3Sopenharmony_ci pa_assert_se(o = i->userdata); 72453a5a1b3Sopenharmony_ci 72553a5a1b3Sopenharmony_ci pa_module_unload_request(o->userdata->module, true); 72653a5a1b3Sopenharmony_ci pa_idxset_remove_by_data(o->userdata->outputs, o, NULL); 72753a5a1b3Sopenharmony_ci output_free(o); 72853a5a1b3Sopenharmony_ci} 72953a5a1b3Sopenharmony_ci 73053a5a1b3Sopenharmony_ci/* Called from thread context */ 73153a5a1b3Sopenharmony_cistatic int sink_input_process_msg(pa_msgobject *obj, int code, void *data, int64_t offset, pa_memchunk *chunk) { 73253a5a1b3Sopenharmony_ci struct output *o = PA_SINK_INPUT(obj)->userdata; 73353a5a1b3Sopenharmony_ci 73453a5a1b3Sopenharmony_ci switch (code) { 73553a5a1b3Sopenharmony_ci 73653a5a1b3Sopenharmony_ci case PA_SINK_INPUT_MESSAGE_GET_LATENCY: { 73753a5a1b3Sopenharmony_ci pa_usec_t *r = data; 73853a5a1b3Sopenharmony_ci 73953a5a1b3Sopenharmony_ci *r = pa_bytes_to_usec(pa_memblockq_get_length(o->memblockq), &o->sink_input->sample_spec); 74053a5a1b3Sopenharmony_ci 74153a5a1b3Sopenharmony_ci /* Fall through, the default handler will add in the extra 74253a5a1b3Sopenharmony_ci * latency added by the resampler */ 74353a5a1b3Sopenharmony_ci break; 74453a5a1b3Sopenharmony_ci } 74553a5a1b3Sopenharmony_ci 74653a5a1b3Sopenharmony_ci case SINK_INPUT_MESSAGE_POST: 74753a5a1b3Sopenharmony_ci 74853a5a1b3Sopenharmony_ci if (o->sink_input->sink->thread_info.state == PA_SINK_RUNNING) { 74953a5a1b3Sopenharmony_ci pa_memblockq_push_align(o->memblockq, chunk); 75053a5a1b3Sopenharmony_ci o->receive_counter += chunk->length; 75153a5a1b3Sopenharmony_ci } else 75253a5a1b3Sopenharmony_ci pa_memblockq_flush_write(o->memblockq, true); 75353a5a1b3Sopenharmony_ci 75453a5a1b3Sopenharmony_ci return 0; 75553a5a1b3Sopenharmony_ci 75653a5a1b3Sopenharmony_ci case SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY: { 75753a5a1b3Sopenharmony_ci pa_usec_t latency = (pa_usec_t) offset; 75853a5a1b3Sopenharmony_ci 75953a5a1b3Sopenharmony_ci pa_sink_input_set_requested_latency_within_thread(o->sink_input, latency); 76053a5a1b3Sopenharmony_ci 76153a5a1b3Sopenharmony_ci return 0; 76253a5a1b3Sopenharmony_ci } 76353a5a1b3Sopenharmony_ci 76453a5a1b3Sopenharmony_ci case SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT: { 76553a5a1b3Sopenharmony_ci size_t length; 76653a5a1b3Sopenharmony_ci 76753a5a1b3Sopenharmony_ci length = pa_memblockq_get_length(o->sink_input->thread_info.render_memblockq); 76853a5a1b3Sopenharmony_ci 76953a5a1b3Sopenharmony_ci o->latency_snapshot.output_memblockq_size = pa_memblockq_get_length(o->memblockq); 77053a5a1b3Sopenharmony_ci 77153a5a1b3Sopenharmony_ci /* Add content of memblockq's to sink latency */ 77253a5a1b3Sopenharmony_ci o->latency_snapshot.sink_latency = pa_sink_get_latency_within_thread(o->sink, true) + 77353a5a1b3Sopenharmony_ci pa_bytes_to_usec(length, &o->sink->sample_spec); 77453a5a1b3Sopenharmony_ci /* Add resampler latency */ 77553a5a1b3Sopenharmony_ci o->latency_snapshot.sink_latency += pa_resampler_get_delay_usec(o->sink_input->thread_info.resampler); 77653a5a1b3Sopenharmony_ci 77753a5a1b3Sopenharmony_ci o->latency_snapshot.timestamp = pa_rtclock_now(); 77853a5a1b3Sopenharmony_ci 77953a5a1b3Sopenharmony_ci o->latency_snapshot.receive_counter = o->receive_counter; 78053a5a1b3Sopenharmony_ci 78153a5a1b3Sopenharmony_ci return 0; 78253a5a1b3Sopenharmony_ci } 78353a5a1b3Sopenharmony_ci } 78453a5a1b3Sopenharmony_ci 78553a5a1b3Sopenharmony_ci return pa_sink_input_process_msg(obj, code, data, offset, chunk); 78653a5a1b3Sopenharmony_ci} 78753a5a1b3Sopenharmony_ci 78853a5a1b3Sopenharmony_ci/* Called from main context */ 78953a5a1b3Sopenharmony_cistatic void suspend(struct userdata *u) { 79053a5a1b3Sopenharmony_ci struct output *o; 79153a5a1b3Sopenharmony_ci uint32_t idx; 79253a5a1b3Sopenharmony_ci 79353a5a1b3Sopenharmony_ci pa_assert(u); 79453a5a1b3Sopenharmony_ci 79553a5a1b3Sopenharmony_ci /* Let's suspend by unlinking all streams */ 79653a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) 79753a5a1b3Sopenharmony_ci output_disable(o); 79853a5a1b3Sopenharmony_ci 79953a5a1b3Sopenharmony_ci pa_log_info("Device suspended..."); 80053a5a1b3Sopenharmony_ci} 80153a5a1b3Sopenharmony_ci 80253a5a1b3Sopenharmony_ci/* Called from main context */ 80353a5a1b3Sopenharmony_cistatic void unsuspend(struct userdata *u) { 80453a5a1b3Sopenharmony_ci struct output *o; 80553a5a1b3Sopenharmony_ci uint32_t idx; 80653a5a1b3Sopenharmony_ci 80753a5a1b3Sopenharmony_ci pa_assert(u); 80853a5a1b3Sopenharmony_ci 80953a5a1b3Sopenharmony_ci /* Let's resume */ 81053a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) 81153a5a1b3Sopenharmony_ci output_enable(o); 81253a5a1b3Sopenharmony_ci 81353a5a1b3Sopenharmony_ci pa_log_info("Resumed successfully..."); 81453a5a1b3Sopenharmony_ci} 81553a5a1b3Sopenharmony_ci 81653a5a1b3Sopenharmony_ci/* Called from main context */ 81753a5a1b3Sopenharmony_cistatic int sink_set_state_in_main_thread_cb(pa_sink *sink, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) { 81853a5a1b3Sopenharmony_ci struct userdata *u; 81953a5a1b3Sopenharmony_ci 82053a5a1b3Sopenharmony_ci pa_sink_assert_ref(sink); 82153a5a1b3Sopenharmony_ci pa_assert_se(u = sink->userdata); 82253a5a1b3Sopenharmony_ci 82353a5a1b3Sopenharmony_ci /* It may be that only the suspend cause is changing, in which 82453a5a1b3Sopenharmony_ci * case there's nothing to do. */ 82553a5a1b3Sopenharmony_ci if (state == u->sink->state) 82653a5a1b3Sopenharmony_ci return 0; 82753a5a1b3Sopenharmony_ci 82853a5a1b3Sopenharmony_ci /* Please note that in contrast to the ALSA modules we call 82953a5a1b3Sopenharmony_ci * suspend/unsuspend from main context here! */ 83053a5a1b3Sopenharmony_ci 83153a5a1b3Sopenharmony_ci switch (state) { 83253a5a1b3Sopenharmony_ci case PA_SINK_SUSPENDED: 83353a5a1b3Sopenharmony_ci pa_assert(PA_SINK_IS_OPENED(u->sink->state)); 83453a5a1b3Sopenharmony_ci 83553a5a1b3Sopenharmony_ci suspend(u); 83653a5a1b3Sopenharmony_ci break; 83753a5a1b3Sopenharmony_ci 83853a5a1b3Sopenharmony_ci case PA_SINK_IDLE: 83953a5a1b3Sopenharmony_ci case PA_SINK_RUNNING: 84053a5a1b3Sopenharmony_ci 84153a5a1b3Sopenharmony_ci if (u->sink->state == PA_SINK_SUSPENDED) 84253a5a1b3Sopenharmony_ci unsuspend(u); 84353a5a1b3Sopenharmony_ci 84453a5a1b3Sopenharmony_ci /* The first smoother update should be done early, otherwise the smoother will 84553a5a1b3Sopenharmony_ci * not be aware of the slave sink latencies and report far too small values. 84653a5a1b3Sopenharmony_ci * This is especially important if after an unsuspend the sink runs on a different 84753a5a1b3Sopenharmony_ci * latency than before. */ 84853a5a1b3Sopenharmony_ci if (state == PA_SINK_RUNNING && !u->time_event && u->adjust_time > 0) 84953a5a1b3Sopenharmony_ci u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + pa_sink_get_requested_latency(u->sink), time_callback, u); 85053a5a1b3Sopenharmony_ci 85153a5a1b3Sopenharmony_ci break; 85253a5a1b3Sopenharmony_ci 85353a5a1b3Sopenharmony_ci case PA_SINK_UNLINKED: 85453a5a1b3Sopenharmony_ci case PA_SINK_INIT: 85553a5a1b3Sopenharmony_ci case PA_SINK_INVALID_STATE: 85653a5a1b3Sopenharmony_ci ; 85753a5a1b3Sopenharmony_ci } 85853a5a1b3Sopenharmony_ci 85953a5a1b3Sopenharmony_ci return 0; 86053a5a1b3Sopenharmony_ci} 86153a5a1b3Sopenharmony_ci 86253a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 86353a5a1b3Sopenharmony_cistatic int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state, pa_suspend_cause_t new_suspend_cause) { 86453a5a1b3Sopenharmony_ci struct userdata *u; 86553a5a1b3Sopenharmony_ci bool running; 86653a5a1b3Sopenharmony_ci 86753a5a1b3Sopenharmony_ci pa_assert(s); 86853a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 86953a5a1b3Sopenharmony_ci 87053a5a1b3Sopenharmony_ci /* It may be that only the suspend cause is changing, in which case there's 87153a5a1b3Sopenharmony_ci * nothing to do. */ 87253a5a1b3Sopenharmony_ci if (new_state == s->thread_info.state) 87353a5a1b3Sopenharmony_ci return 0; 87453a5a1b3Sopenharmony_ci 87553a5a1b3Sopenharmony_ci running = new_state == PA_SINK_RUNNING; 87653a5a1b3Sopenharmony_ci pa_atomic_store(&u->thread_info.running, running); 87753a5a1b3Sopenharmony_ci 87853a5a1b3Sopenharmony_ci if (running) { 87953a5a1b3Sopenharmony_ci u->thread_info.render_timestamp = 0; 88053a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 88153a5a1b3Sopenharmony_ci pa_smoother_2_resume(u->thread_info.smoother, pa_rtclock_now()); 88253a5a1b3Sopenharmony_ci } else 88353a5a1b3Sopenharmony_ci pa_smoother_2_pause(u->thread_info.smoother, pa_rtclock_now()); 88453a5a1b3Sopenharmony_ci#else 88553a5a1b3Sopenharmony_ci pa_smoother_resume(u->thread_info.smoother, pa_rtclock_now(), true); 88653a5a1b3Sopenharmony_ci } else 88753a5a1b3Sopenharmony_ci pa_smoother_pause(u->thread_info.smoother, pa_rtclock_now()); 88853a5a1b3Sopenharmony_ci#endif 88953a5a1b3Sopenharmony_ci 89053a5a1b3Sopenharmony_ci return 0; 89153a5a1b3Sopenharmony_ci} 89253a5a1b3Sopenharmony_ci 89353a5a1b3Sopenharmony_ci/* Called from IO context */ 89453a5a1b3Sopenharmony_cistatic void update_max_request(struct userdata *u) { 89553a5a1b3Sopenharmony_ci size_t max_request = 0; 89653a5a1b3Sopenharmony_ci struct output *o; 89753a5a1b3Sopenharmony_ci 89853a5a1b3Sopenharmony_ci pa_assert(u); 89953a5a1b3Sopenharmony_ci pa_sink_assert_io_context(u->sink); 90053a5a1b3Sopenharmony_ci 90153a5a1b3Sopenharmony_ci /* Collects the max_request values of all streams and sets the 90253a5a1b3Sopenharmony_ci * largest one locally */ 90353a5a1b3Sopenharmony_ci 90453a5a1b3Sopenharmony_ci PA_LLIST_FOREACH(o, u->thread_info.active_outputs) { 90553a5a1b3Sopenharmony_ci size_t mr = (size_t) pa_atomic_load(&o->max_request); 90653a5a1b3Sopenharmony_ci 90753a5a1b3Sopenharmony_ci if (mr > max_request) 90853a5a1b3Sopenharmony_ci max_request = mr; 90953a5a1b3Sopenharmony_ci } 91053a5a1b3Sopenharmony_ci 91153a5a1b3Sopenharmony_ci if (max_request <= 0) 91253a5a1b3Sopenharmony_ci max_request = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); 91353a5a1b3Sopenharmony_ci 91453a5a1b3Sopenharmony_ci pa_log_debug("Sink update max request %lu", (unsigned long) max_request); 91553a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, max_request); 91653a5a1b3Sopenharmony_ci} 91753a5a1b3Sopenharmony_ci 91853a5a1b3Sopenharmony_ci/* Called from IO context */ 91953a5a1b3Sopenharmony_cistatic void update_latency_range(struct userdata *u) { 92053a5a1b3Sopenharmony_ci pa_usec_t min_latency = 0, max_latency = (pa_usec_t) -1; 92153a5a1b3Sopenharmony_ci struct output *o; 92253a5a1b3Sopenharmony_ci 92353a5a1b3Sopenharmony_ci pa_assert(u); 92453a5a1b3Sopenharmony_ci pa_sink_assert_io_context(u->sink); 92553a5a1b3Sopenharmony_ci 92653a5a1b3Sopenharmony_ci /* Collects the latency_range values of all streams and sets 92753a5a1b3Sopenharmony_ci * the max of min and min of max locally */ 92853a5a1b3Sopenharmony_ci PA_LLIST_FOREACH(o, u->thread_info.active_outputs) { 92953a5a1b3Sopenharmony_ci pa_usec_t min = (size_t) pa_atomic_load(&o->min_latency); 93053a5a1b3Sopenharmony_ci pa_usec_t max = (size_t) pa_atomic_load(&o->max_latency); 93153a5a1b3Sopenharmony_ci 93253a5a1b3Sopenharmony_ci if (min > min_latency) 93353a5a1b3Sopenharmony_ci min_latency = min; 93453a5a1b3Sopenharmony_ci if (max_latency == (pa_usec_t) -1 || max < max_latency) 93553a5a1b3Sopenharmony_ci max_latency = max; 93653a5a1b3Sopenharmony_ci } 93753a5a1b3Sopenharmony_ci if (max_latency == (pa_usec_t) -1) { 93853a5a1b3Sopenharmony_ci /* No outputs, use default limits. */ 93953a5a1b3Sopenharmony_ci min_latency = u->default_min_latency; 94053a5a1b3Sopenharmony_ci max_latency = u->default_max_latency; 94153a5a1b3Sopenharmony_ci } 94253a5a1b3Sopenharmony_ci 94353a5a1b3Sopenharmony_ci /* As long as we don't support rewinding, we should limit the max latency 94453a5a1b3Sopenharmony_ci * to a conservative value. */ 94553a5a1b3Sopenharmony_ci if (max_latency > u->default_max_latency) 94653a5a1b3Sopenharmony_ci max_latency = u->default_max_latency; 94753a5a1b3Sopenharmony_ci 94853a5a1b3Sopenharmony_ci /* Never ever try to set lower max latency than min latency, it just 94953a5a1b3Sopenharmony_ci * doesn't make sense. */ 95053a5a1b3Sopenharmony_ci if (max_latency < min_latency) 95153a5a1b3Sopenharmony_ci max_latency = min_latency; 95253a5a1b3Sopenharmony_ci 95353a5a1b3Sopenharmony_ci pa_log_debug("Sink update latency range %" PRIu64 " %" PRIu64, min_latency, max_latency); 95453a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, min_latency, max_latency); 95553a5a1b3Sopenharmony_ci} 95653a5a1b3Sopenharmony_ci 95753a5a1b3Sopenharmony_ci/* Called from thread context of the io thread */ 95853a5a1b3Sopenharmony_cistatic void output_add_within_thread(struct output *o) { 95953a5a1b3Sopenharmony_ci pa_assert(o); 96053a5a1b3Sopenharmony_ci pa_sink_assert_io_context(o->sink); 96153a5a1b3Sopenharmony_ci 96253a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(struct output, o->userdata->thread_info.active_outputs, o); 96353a5a1b3Sopenharmony_ci 96453a5a1b3Sopenharmony_ci pa_assert(!o->outq_rtpoll_item_read); 96553a5a1b3Sopenharmony_ci pa_assert(!o->audio_inq_rtpoll_item_write); 96653a5a1b3Sopenharmony_ci pa_assert(!o->control_inq_rtpoll_item_write); 96753a5a1b3Sopenharmony_ci 96853a5a1b3Sopenharmony_ci o->outq_rtpoll_item_read = pa_rtpoll_item_new_asyncmsgq_read( 96953a5a1b3Sopenharmony_ci o->userdata->rtpoll, 97053a5a1b3Sopenharmony_ci PA_RTPOLL_EARLY-1, /* This item is very important */ 97153a5a1b3Sopenharmony_ci o->outq); 97253a5a1b3Sopenharmony_ci o->audio_inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( 97353a5a1b3Sopenharmony_ci o->userdata->rtpoll, 97453a5a1b3Sopenharmony_ci PA_RTPOLL_EARLY, 97553a5a1b3Sopenharmony_ci o->audio_inq); 97653a5a1b3Sopenharmony_ci o->control_inq_rtpoll_item_write = pa_rtpoll_item_new_asyncmsgq_write( 97753a5a1b3Sopenharmony_ci o->userdata->rtpoll, 97853a5a1b3Sopenharmony_ci PA_RTPOLL_NORMAL, 97953a5a1b3Sopenharmony_ci o->control_inq); 98053a5a1b3Sopenharmony_ci o->receive_counter = o->userdata->thread_info.counter; 98153a5a1b3Sopenharmony_ci} 98253a5a1b3Sopenharmony_ci 98353a5a1b3Sopenharmony_ci/* Called from thread context of the io thread */ 98453a5a1b3Sopenharmony_cistatic void output_remove_within_thread(struct output *o) { 98553a5a1b3Sopenharmony_ci pa_assert(o); 98653a5a1b3Sopenharmony_ci pa_sink_assert_io_context(o->sink); 98753a5a1b3Sopenharmony_ci 98853a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(struct output, o->userdata->thread_info.active_outputs, o); 98953a5a1b3Sopenharmony_ci 99053a5a1b3Sopenharmony_ci if (o->outq_rtpoll_item_read) { 99153a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->outq_rtpoll_item_read); 99253a5a1b3Sopenharmony_ci o->outq_rtpoll_item_read = NULL; 99353a5a1b3Sopenharmony_ci } 99453a5a1b3Sopenharmony_ci 99553a5a1b3Sopenharmony_ci if (o->audio_inq_rtpoll_item_write) { 99653a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->audio_inq_rtpoll_item_write); 99753a5a1b3Sopenharmony_ci o->audio_inq_rtpoll_item_write = NULL; 99853a5a1b3Sopenharmony_ci } 99953a5a1b3Sopenharmony_ci 100053a5a1b3Sopenharmony_ci if (o->control_inq_rtpoll_item_write) { 100153a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->control_inq_rtpoll_item_write); 100253a5a1b3Sopenharmony_ci o->control_inq_rtpoll_item_write = NULL; 100353a5a1b3Sopenharmony_ci } 100453a5a1b3Sopenharmony_ci} 100553a5a1b3Sopenharmony_ci 100653a5a1b3Sopenharmony_ci/* Called from sink I/O thread context */ 100753a5a1b3Sopenharmony_cistatic void sink_update_requested_latency(pa_sink *s) { 100853a5a1b3Sopenharmony_ci struct userdata *u; 100953a5a1b3Sopenharmony_ci struct output *o; 101053a5a1b3Sopenharmony_ci 101153a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 101253a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 101353a5a1b3Sopenharmony_ci 101453a5a1b3Sopenharmony_ci u->block_usec = pa_sink_get_requested_latency_within_thread(s); 101553a5a1b3Sopenharmony_ci 101653a5a1b3Sopenharmony_ci if (u->block_usec == (pa_usec_t) -1) 101753a5a1b3Sopenharmony_ci u->block_usec = s->thread_info.max_latency; 101853a5a1b3Sopenharmony_ci 101953a5a1b3Sopenharmony_ci pa_log_debug("Sink update requested latency %0.2f", (double) u->block_usec / PA_USEC_PER_MSEC); 102053a5a1b3Sopenharmony_ci 102153a5a1b3Sopenharmony_ci /* Just hand this one over to all sink_inputs */ 102253a5a1b3Sopenharmony_ci PA_LLIST_FOREACH(o, u->thread_info.active_outputs) { 102353a5a1b3Sopenharmony_ci pa_asyncmsgq_post(o->control_inq, PA_MSGOBJECT(o->sink_input), SINK_INPUT_MESSAGE_SET_REQUESTED_LATENCY, NULL, 102453a5a1b3Sopenharmony_ci u->block_usec, NULL, NULL); 102553a5a1b3Sopenharmony_ci } 102653a5a1b3Sopenharmony_ci} 102753a5a1b3Sopenharmony_ci 102853a5a1b3Sopenharmony_ci 102953a5a1b3Sopenharmony_ci/* Called from thread context of the io thread */ 103053a5a1b3Sopenharmony_cistatic int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 103153a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK(o)->userdata; 103253a5a1b3Sopenharmony_ci 103353a5a1b3Sopenharmony_ci switch (code) { 103453a5a1b3Sopenharmony_ci 103553a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: { 103653a5a1b3Sopenharmony_ci int64_t *delay = data; 103753a5a1b3Sopenharmony_ci 103853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 103953a5a1b3Sopenharmony_ci *delay = pa_smoother_2_get_delay(u->thread_info.smoother, pa_rtclock_now(), u->thread_info.counter); 104053a5a1b3Sopenharmony_ci#else 104153a5a1b3Sopenharmony_ci pa_usec_t x, y, c; 104253a5a1b3Sopenharmony_ci 104353a5a1b3Sopenharmony_ci x = pa_rtclock_now(); 104453a5a1b3Sopenharmony_ci y = pa_smoother_get(u->thread_info.smoother, x); 104553a5a1b3Sopenharmony_ci 104653a5a1b3Sopenharmony_ci c = pa_bytes_to_usec(u->thread_info.counter, &u->sink->sample_spec); 104753a5a1b3Sopenharmony_ci 104853a5a1b3Sopenharmony_ci *delay = (int64_t)c - y; 104953a5a1b3Sopenharmony_ci#endif 105053a5a1b3Sopenharmony_ci 105153a5a1b3Sopenharmony_ci return 0; 105253a5a1b3Sopenharmony_ci } 105353a5a1b3Sopenharmony_ci 105453a5a1b3Sopenharmony_ci case SINK_MESSAGE_ADD_OUTPUT: 105553a5a1b3Sopenharmony_ci output_add_within_thread(data); 105653a5a1b3Sopenharmony_ci update_max_request(u); 105753a5a1b3Sopenharmony_ci update_latency_range(u); 105853a5a1b3Sopenharmony_ci return 0; 105953a5a1b3Sopenharmony_ci 106053a5a1b3Sopenharmony_ci case SINK_MESSAGE_REMOVE_OUTPUT: 106153a5a1b3Sopenharmony_ci output_remove_within_thread(data); 106253a5a1b3Sopenharmony_ci update_max_request(u); 106353a5a1b3Sopenharmony_ci update_latency_range(u); 106453a5a1b3Sopenharmony_ci return 0; 106553a5a1b3Sopenharmony_ci 106653a5a1b3Sopenharmony_ci case SINK_MESSAGE_NEED: 106753a5a1b3Sopenharmony_ci render_memblock(u, (struct output*) data, (size_t) offset); 106853a5a1b3Sopenharmony_ci return 0; 106953a5a1b3Sopenharmony_ci 107053a5a1b3Sopenharmony_ci case SINK_MESSAGE_UPDATE_LATENCY: { 107153a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 107253a5a1b3Sopenharmony_ci size_t latency; 107353a5a1b3Sopenharmony_ci 107453a5a1b3Sopenharmony_ci latency = pa_usec_to_bytes((pa_usec_t)offset, &u->sink->sample_spec); 107553a5a1b3Sopenharmony_ci pa_smoother_2_put(u->thread_info.smoother, u->thread_info.snapshot_time, (int64_t)u->thread_info.snapshot_counter - latency); 107653a5a1b3Sopenharmony_ci#else 107753a5a1b3Sopenharmony_ci pa_usec_t x, y, latency = (pa_usec_t) offset; 107853a5a1b3Sopenharmony_ci 107953a5a1b3Sopenharmony_ci /* It may be possible that thread_info.counter has been increased 108053a5a1b3Sopenharmony_ci * since we took the snapshot. Therefore we have to use the snapshot 108153a5a1b3Sopenharmony_ci * time and counter instead of the current values. */ 108253a5a1b3Sopenharmony_ci x = u->thread_info.snapshot_time; 108353a5a1b3Sopenharmony_ci y = pa_bytes_to_usec(u->thread_info.snapshot_counter, &u->sink->sample_spec); 108453a5a1b3Sopenharmony_ci 108553a5a1b3Sopenharmony_ci if (y > latency) 108653a5a1b3Sopenharmony_ci y -= latency; 108753a5a1b3Sopenharmony_ci else 108853a5a1b3Sopenharmony_ci y = 0; 108953a5a1b3Sopenharmony_ci 109053a5a1b3Sopenharmony_ci pa_smoother_put(u->thread_info.smoother, x, y); 109153a5a1b3Sopenharmony_ci#endif 109253a5a1b3Sopenharmony_ci return 0; 109353a5a1b3Sopenharmony_ci } 109453a5a1b3Sopenharmony_ci 109553a5a1b3Sopenharmony_ci case SINK_MESSAGE_GET_SNAPSHOT: { 109653a5a1b3Sopenharmony_ci struct sink_snapshot *rdata = data; 109753a5a1b3Sopenharmony_ci 109853a5a1b3Sopenharmony_ci rdata->timestamp = u->thread_info.render_timestamp; 109953a5a1b3Sopenharmony_ci rdata->send_counter = u->thread_info.counter; 110053a5a1b3Sopenharmony_ci u->thread_info.snapshot_counter = u->thread_info.counter; 110153a5a1b3Sopenharmony_ci u->thread_info.snapshot_time = u->thread_info.render_timestamp; 110253a5a1b3Sopenharmony_ci 110353a5a1b3Sopenharmony_ci return 0; 110453a5a1b3Sopenharmony_ci } 110553a5a1b3Sopenharmony_ci 110653a5a1b3Sopenharmony_ci case SINK_MESSAGE_UPDATE_MAX_REQUEST: 110753a5a1b3Sopenharmony_ci update_max_request(u); 110853a5a1b3Sopenharmony_ci break; 110953a5a1b3Sopenharmony_ci 111053a5a1b3Sopenharmony_ci case SINK_MESSAGE_UPDATE_LATENCY_RANGE: 111153a5a1b3Sopenharmony_ci update_latency_range(u); 111253a5a1b3Sopenharmony_ci break; 111353a5a1b3Sopenharmony_ci 111453a5a1b3Sopenharmony_ci} 111553a5a1b3Sopenharmony_ci 111653a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 111753a5a1b3Sopenharmony_ci} 111853a5a1b3Sopenharmony_ci 111953a5a1b3Sopenharmony_cistatic void update_description(struct userdata *u) { 112053a5a1b3Sopenharmony_ci bool first = true; 112153a5a1b3Sopenharmony_ci char *t; 112253a5a1b3Sopenharmony_ci struct output *o; 112353a5a1b3Sopenharmony_ci uint32_t idx; 112453a5a1b3Sopenharmony_ci 112553a5a1b3Sopenharmony_ci pa_assert(u); 112653a5a1b3Sopenharmony_ci 112753a5a1b3Sopenharmony_ci if (!u->auto_desc) 112853a5a1b3Sopenharmony_ci return; 112953a5a1b3Sopenharmony_ci 113053a5a1b3Sopenharmony_ci if (pa_idxset_isempty(u->outputs)) { 113153a5a1b3Sopenharmony_ci pa_sink_set_description(u->sink, "Simultaneous output"); 113253a5a1b3Sopenharmony_ci return; 113353a5a1b3Sopenharmony_ci } 113453a5a1b3Sopenharmony_ci 113553a5a1b3Sopenharmony_ci t = pa_xstrdup("Simultaneous output to"); 113653a5a1b3Sopenharmony_ci 113753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) { 113853a5a1b3Sopenharmony_ci char *e; 113953a5a1b3Sopenharmony_ci 114053a5a1b3Sopenharmony_ci if (first) { 114153a5a1b3Sopenharmony_ci e = pa_sprintf_malloc("%s %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); 114253a5a1b3Sopenharmony_ci first = false; 114353a5a1b3Sopenharmony_ci } else 114453a5a1b3Sopenharmony_ci e = pa_sprintf_malloc("%s, %s", t, pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); 114553a5a1b3Sopenharmony_ci 114653a5a1b3Sopenharmony_ci pa_xfree(t); 114753a5a1b3Sopenharmony_ci t = e; 114853a5a1b3Sopenharmony_ci } 114953a5a1b3Sopenharmony_ci 115053a5a1b3Sopenharmony_ci pa_sink_set_description(u->sink, t); 115153a5a1b3Sopenharmony_ci pa_xfree(t); 115253a5a1b3Sopenharmony_ci} 115353a5a1b3Sopenharmony_ci 115453a5a1b3Sopenharmony_cistatic int output_create_sink_input(struct output *o) { 115553a5a1b3Sopenharmony_ci struct userdata *u; 115653a5a1b3Sopenharmony_ci pa_sink_input_new_data data; 115753a5a1b3Sopenharmony_ci 115853a5a1b3Sopenharmony_ci pa_assert(o); 115953a5a1b3Sopenharmony_ci 116053a5a1b3Sopenharmony_ci if (o->sink_input) 116153a5a1b3Sopenharmony_ci return 0; 116253a5a1b3Sopenharmony_ci 116353a5a1b3Sopenharmony_ci u = o->userdata; 116453a5a1b3Sopenharmony_ci 116553a5a1b3Sopenharmony_ci pa_sink_input_new_data_init(&data); 116653a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sink(&data, o->sink, false, true); 116753a5a1b3Sopenharmony_ci data.driver = __FILE__; 116853a5a1b3Sopenharmony_ci pa_proplist_setf(data.proplist, PA_PROP_MEDIA_NAME, "Simultaneous output on %s", pa_strnull(pa_proplist_gets(o->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); 116953a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 117053a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sample_spec(&data, &u->sink->sample_spec); 117153a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_channel_map(&data, &u->sink->channel_map); 117253a5a1b3Sopenharmony_ci data.module = u->module; 117353a5a1b3Sopenharmony_ci data.resample_method = u->resample_method; 117453a5a1b3Sopenharmony_ci data.flags = PA_SINK_INPUT_VARIABLE_RATE|PA_SINK_INPUT_DONT_MOVE|PA_SINK_INPUT_NO_CREATE_ON_SUSPEND; 117553a5a1b3Sopenharmony_ci data.origin_sink = u->sink; 117653a5a1b3Sopenharmony_ci 117753a5a1b3Sopenharmony_ci if (!u->remix) 117853a5a1b3Sopenharmony_ci data.flags |= PA_SINK_INPUT_NO_REMIX; 117953a5a1b3Sopenharmony_ci 118053a5a1b3Sopenharmony_ci pa_sink_input_new(&o->sink_input, u->core, &data); 118153a5a1b3Sopenharmony_ci 118253a5a1b3Sopenharmony_ci pa_sink_input_new_data_done(&data); 118353a5a1b3Sopenharmony_ci 118453a5a1b3Sopenharmony_ci if (!o->sink_input) 118553a5a1b3Sopenharmony_ci return -1; 118653a5a1b3Sopenharmony_ci 118753a5a1b3Sopenharmony_ci o->sink_input->parent.process_msg = sink_input_process_msg; 118853a5a1b3Sopenharmony_ci o->sink_input->pop = sink_input_pop_cb; 118953a5a1b3Sopenharmony_ci o->sink_input->process_rewind = sink_input_process_rewind_cb; 119053a5a1b3Sopenharmony_ci o->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 119153a5a1b3Sopenharmony_ci o->sink_input->update_max_request = sink_input_update_max_request_cb; 119253a5a1b3Sopenharmony_ci o->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; 119353a5a1b3Sopenharmony_ci o->sink_input->attach = sink_input_attach_cb; 119453a5a1b3Sopenharmony_ci o->sink_input->detach = sink_input_detach_cb; 119553a5a1b3Sopenharmony_ci o->sink_input->kill = sink_input_kill_cb; 119653a5a1b3Sopenharmony_ci o->sink_input->userdata = o; 119753a5a1b3Sopenharmony_ci 119853a5a1b3Sopenharmony_ci pa_sink_input_set_requested_latency(o->sink_input, pa_sink_get_requested_latency(u->sink)); 119953a5a1b3Sopenharmony_ci 120053a5a1b3Sopenharmony_ci return 0; 120153a5a1b3Sopenharmony_ci} 120253a5a1b3Sopenharmony_ci 120353a5a1b3Sopenharmony_ci/* Called from main context */ 120453a5a1b3Sopenharmony_cistatic struct output *output_new(struct userdata *u, pa_sink *sink) { 120553a5a1b3Sopenharmony_ci struct output *o; 120653a5a1b3Sopenharmony_ci 120753a5a1b3Sopenharmony_ci pa_assert(u); 120853a5a1b3Sopenharmony_ci pa_assert(sink); 120953a5a1b3Sopenharmony_ci pa_assert(u->sink); 121053a5a1b3Sopenharmony_ci 121153a5a1b3Sopenharmony_ci o = pa_xnew0(struct output, 1); 121253a5a1b3Sopenharmony_ci o->userdata = u; 121353a5a1b3Sopenharmony_ci 121453a5a1b3Sopenharmony_ci o->audio_inq = pa_asyncmsgq_new(0); 121553a5a1b3Sopenharmony_ci if (!o->audio_inq) { 121653a5a1b3Sopenharmony_ci pa_log("pa_asyncmsgq_new() failed."); 121753a5a1b3Sopenharmony_ci goto fail; 121853a5a1b3Sopenharmony_ci } 121953a5a1b3Sopenharmony_ci 122053a5a1b3Sopenharmony_ci o->control_inq = pa_asyncmsgq_new(0); 122153a5a1b3Sopenharmony_ci if (!o->control_inq) { 122253a5a1b3Sopenharmony_ci pa_log("pa_asyncmsgq_new() failed."); 122353a5a1b3Sopenharmony_ci goto fail; 122453a5a1b3Sopenharmony_ci } 122553a5a1b3Sopenharmony_ci 122653a5a1b3Sopenharmony_ci o->outq = pa_asyncmsgq_new(0); 122753a5a1b3Sopenharmony_ci if (!o->outq) { 122853a5a1b3Sopenharmony_ci pa_log("pa_asyncmsgq_new() failed."); 122953a5a1b3Sopenharmony_ci goto fail; 123053a5a1b3Sopenharmony_ci } 123153a5a1b3Sopenharmony_ci 123253a5a1b3Sopenharmony_ci o->sink = sink; 123353a5a1b3Sopenharmony_ci o->memblockq = pa_memblockq_new( 123453a5a1b3Sopenharmony_ci "module-combine-sink output memblockq", 123553a5a1b3Sopenharmony_ci 0, 123653a5a1b3Sopenharmony_ci MEMBLOCKQ_MAXLENGTH, 123753a5a1b3Sopenharmony_ci MEMBLOCKQ_MAXLENGTH, 123853a5a1b3Sopenharmony_ci &u->sink->sample_spec, 123953a5a1b3Sopenharmony_ci 1, 124053a5a1b3Sopenharmony_ci 0, 124153a5a1b3Sopenharmony_ci 0, 124253a5a1b3Sopenharmony_ci &u->sink->silence); 124353a5a1b3Sopenharmony_ci 124453a5a1b3Sopenharmony_ci pa_assert_se(pa_idxset_put(u->outputs, o, NULL) == 0); 124553a5a1b3Sopenharmony_ci update_description(u); 124653a5a1b3Sopenharmony_ci 124753a5a1b3Sopenharmony_ci return o; 124853a5a1b3Sopenharmony_ci 124953a5a1b3Sopenharmony_cifail: 125053a5a1b3Sopenharmony_ci output_free(o); 125153a5a1b3Sopenharmony_ci 125253a5a1b3Sopenharmony_ci return NULL; 125353a5a1b3Sopenharmony_ci} 125453a5a1b3Sopenharmony_ci 125553a5a1b3Sopenharmony_ci/* Called from main context */ 125653a5a1b3Sopenharmony_cistatic void output_free(struct output *o) { 125753a5a1b3Sopenharmony_ci pa_assert(o); 125853a5a1b3Sopenharmony_ci 125953a5a1b3Sopenharmony_ci output_disable(o); 126053a5a1b3Sopenharmony_ci update_description(o->userdata); 126153a5a1b3Sopenharmony_ci 126253a5a1b3Sopenharmony_ci if (o->audio_inq_rtpoll_item_read) 126353a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->audio_inq_rtpoll_item_read); 126453a5a1b3Sopenharmony_ci if (o->audio_inq_rtpoll_item_write) 126553a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->audio_inq_rtpoll_item_write); 126653a5a1b3Sopenharmony_ci 126753a5a1b3Sopenharmony_ci if (o->control_inq_rtpoll_item_read) 126853a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->control_inq_rtpoll_item_read); 126953a5a1b3Sopenharmony_ci if (o->control_inq_rtpoll_item_write) 127053a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->control_inq_rtpoll_item_write); 127153a5a1b3Sopenharmony_ci 127253a5a1b3Sopenharmony_ci if (o->outq_rtpoll_item_read) 127353a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->outq_rtpoll_item_read); 127453a5a1b3Sopenharmony_ci if (o->outq_rtpoll_item_write) 127553a5a1b3Sopenharmony_ci pa_rtpoll_item_free(o->outq_rtpoll_item_write); 127653a5a1b3Sopenharmony_ci 127753a5a1b3Sopenharmony_ci if (o->audio_inq) 127853a5a1b3Sopenharmony_ci pa_asyncmsgq_unref(o->audio_inq); 127953a5a1b3Sopenharmony_ci 128053a5a1b3Sopenharmony_ci if (o->control_inq) 128153a5a1b3Sopenharmony_ci pa_asyncmsgq_unref(o->control_inq); 128253a5a1b3Sopenharmony_ci 128353a5a1b3Sopenharmony_ci if (o->outq) 128453a5a1b3Sopenharmony_ci pa_asyncmsgq_unref(o->outq); 128553a5a1b3Sopenharmony_ci 128653a5a1b3Sopenharmony_ci if (o->memblockq) 128753a5a1b3Sopenharmony_ci pa_memblockq_free(o->memblockq); 128853a5a1b3Sopenharmony_ci 128953a5a1b3Sopenharmony_ci pa_xfree(o); 129053a5a1b3Sopenharmony_ci} 129153a5a1b3Sopenharmony_ci 129253a5a1b3Sopenharmony_ci/* Called from main context */ 129353a5a1b3Sopenharmony_cistatic void output_enable(struct output *o) { 129453a5a1b3Sopenharmony_ci pa_assert(o); 129553a5a1b3Sopenharmony_ci 129653a5a1b3Sopenharmony_ci if (o->sink_input) 129753a5a1b3Sopenharmony_ci return; 129853a5a1b3Sopenharmony_ci 129953a5a1b3Sopenharmony_ci /* This might cause the sink to be resumed. The state change hook 130053a5a1b3Sopenharmony_ci * of the sink might hence be called from here, which might then 130153a5a1b3Sopenharmony_ci * cause us to be called in a loop. Make sure that state changes 130253a5a1b3Sopenharmony_ci * for this output don't cause this loop by setting a flag here */ 130353a5a1b3Sopenharmony_ci o->ignore_state_change = true; 130453a5a1b3Sopenharmony_ci 130553a5a1b3Sopenharmony_ci if (output_create_sink_input(o) >= 0) { 130653a5a1b3Sopenharmony_ci 130753a5a1b3Sopenharmony_ci if (o->sink->state != PA_SINK_INIT) { 130853a5a1b3Sopenharmony_ci /* Enable the sink input. That means that the sink 130953a5a1b3Sopenharmony_ci * is now asked for new data. */ 131053a5a1b3Sopenharmony_ci pa_sink_input_put(o->sink_input); 131153a5a1b3Sopenharmony_ci } 131253a5a1b3Sopenharmony_ci } 131353a5a1b3Sopenharmony_ci 131453a5a1b3Sopenharmony_ci o->ignore_state_change = false; 131553a5a1b3Sopenharmony_ci} 131653a5a1b3Sopenharmony_ci 131753a5a1b3Sopenharmony_ci/* Called from main context */ 131853a5a1b3Sopenharmony_cistatic void output_disable(struct output *o) { 131953a5a1b3Sopenharmony_ci pa_assert(o); 132053a5a1b3Sopenharmony_ci 132153a5a1b3Sopenharmony_ci if (!o->sink_input) 132253a5a1b3Sopenharmony_ci return; 132353a5a1b3Sopenharmony_ci 132453a5a1b3Sopenharmony_ci /* We disable the sink input. That means that the sink is 132553a5a1b3Sopenharmony_ci * not asked for new data anymore */ 132653a5a1b3Sopenharmony_ci pa_sink_input_unlink(o->sink_input); 132753a5a1b3Sopenharmony_ci 132853a5a1b3Sopenharmony_ci /* Now deallocate the stream */ 132953a5a1b3Sopenharmony_ci pa_sink_input_unref(o->sink_input); 133053a5a1b3Sopenharmony_ci o->sink_input = NULL; 133153a5a1b3Sopenharmony_ci 133253a5a1b3Sopenharmony_ci /* Finally, drop all queued data */ 133353a5a1b3Sopenharmony_ci pa_memblockq_flush_write(o->memblockq, true); 133453a5a1b3Sopenharmony_ci pa_asyncmsgq_flush(o->audio_inq, false); 133553a5a1b3Sopenharmony_ci pa_asyncmsgq_flush(o->control_inq, false); 133653a5a1b3Sopenharmony_ci pa_asyncmsgq_flush(o->outq, false); 133753a5a1b3Sopenharmony_ci} 133853a5a1b3Sopenharmony_ci 133953a5a1b3Sopenharmony_ci/* Called from main context */ 134053a5a1b3Sopenharmony_cistatic void output_verify(struct output *o) { 134153a5a1b3Sopenharmony_ci pa_assert(o); 134253a5a1b3Sopenharmony_ci 134353a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(o->userdata->sink->state)) 134453a5a1b3Sopenharmony_ci output_enable(o); 134553a5a1b3Sopenharmony_ci else 134653a5a1b3Sopenharmony_ci output_disable(o); 134753a5a1b3Sopenharmony_ci} 134853a5a1b3Sopenharmony_ci 134953a5a1b3Sopenharmony_ci/* Called from main context */ 135053a5a1b3Sopenharmony_cistatic bool is_suitable_sink(struct userdata *u, pa_sink *s) { 135153a5a1b3Sopenharmony_ci const char *t; 135253a5a1b3Sopenharmony_ci 135353a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 135453a5a1b3Sopenharmony_ci 135553a5a1b3Sopenharmony_ci if (s == u->sink) 135653a5a1b3Sopenharmony_ci return false; 135753a5a1b3Sopenharmony_ci 135853a5a1b3Sopenharmony_ci if (!(s->flags & PA_SINK_HARDWARE)) 135953a5a1b3Sopenharmony_ci return false; 136053a5a1b3Sopenharmony_ci 136153a5a1b3Sopenharmony_ci if (!(s->flags & PA_SINK_LATENCY)) 136253a5a1b3Sopenharmony_ci return false; 136353a5a1b3Sopenharmony_ci 136453a5a1b3Sopenharmony_ci if ((t = pa_proplist_gets(s->proplist, PA_PROP_DEVICE_CLASS))) 136553a5a1b3Sopenharmony_ci if (!pa_streq(t, "sound")) 136653a5a1b3Sopenharmony_ci return false; 136753a5a1b3Sopenharmony_ci 136853a5a1b3Sopenharmony_ci return true; 136953a5a1b3Sopenharmony_ci} 137053a5a1b3Sopenharmony_ci 137153a5a1b3Sopenharmony_ci/* Called from main context */ 137253a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_put_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { 137353a5a1b3Sopenharmony_ci struct output *o; 137453a5a1b3Sopenharmony_ci 137553a5a1b3Sopenharmony_ci pa_core_assert_ref(c); 137653a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 137753a5a1b3Sopenharmony_ci pa_assert(u); 137853a5a1b3Sopenharmony_ci 137953a5a1b3Sopenharmony_ci if (u->automatic) { 138053a5a1b3Sopenharmony_ci if (!is_suitable_sink(u, s)) 138153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 138253a5a1b3Sopenharmony_ci } else { 138353a5a1b3Sopenharmony_ci /* Check if the sink is a previously unlinked slave (non-automatic mode) */ 138453a5a1b3Sopenharmony_ci pa_strlist *l = u->unlinked_slaves; 138553a5a1b3Sopenharmony_ci 138653a5a1b3Sopenharmony_ci while (l && !pa_streq(pa_strlist_data(l), s->name)) 138753a5a1b3Sopenharmony_ci l = pa_strlist_next(l); 138853a5a1b3Sopenharmony_ci 138953a5a1b3Sopenharmony_ci if (!l) 139053a5a1b3Sopenharmony_ci return PA_HOOK_OK; 139153a5a1b3Sopenharmony_ci 139253a5a1b3Sopenharmony_ci u->unlinked_slaves = pa_strlist_remove(u->unlinked_slaves, s->name); 139353a5a1b3Sopenharmony_ci } 139453a5a1b3Sopenharmony_ci 139553a5a1b3Sopenharmony_ci pa_log_info("Configuring new sink: %s", s->name); 139653a5a1b3Sopenharmony_ci if (!(o = output_new(u, s))) { 139753a5a1b3Sopenharmony_ci pa_log("Failed to create sink input on sink '%s'.", s->name); 139853a5a1b3Sopenharmony_ci return PA_HOOK_OK; 139953a5a1b3Sopenharmony_ci } 140053a5a1b3Sopenharmony_ci 140153a5a1b3Sopenharmony_ci output_verify(o); 140253a5a1b3Sopenharmony_ci 140353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 140453a5a1b3Sopenharmony_ci} 140553a5a1b3Sopenharmony_ci 140653a5a1b3Sopenharmony_ci/* Called from main context */ 140753a5a1b3Sopenharmony_cistatic struct output* find_output(struct userdata *u, pa_sink *s) { 140853a5a1b3Sopenharmony_ci struct output *o; 140953a5a1b3Sopenharmony_ci uint32_t idx; 141053a5a1b3Sopenharmony_ci 141153a5a1b3Sopenharmony_ci pa_assert(u); 141253a5a1b3Sopenharmony_ci pa_assert(s); 141353a5a1b3Sopenharmony_ci 141453a5a1b3Sopenharmony_ci if (u->sink == s) 141553a5a1b3Sopenharmony_ci return NULL; 141653a5a1b3Sopenharmony_ci 141753a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) 141853a5a1b3Sopenharmony_ci if (o->sink == s) 141953a5a1b3Sopenharmony_ci return o; 142053a5a1b3Sopenharmony_ci 142153a5a1b3Sopenharmony_ci return NULL; 142253a5a1b3Sopenharmony_ci} 142353a5a1b3Sopenharmony_ci 142453a5a1b3Sopenharmony_ci/* Called from main context */ 142553a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_unlink_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { 142653a5a1b3Sopenharmony_ci struct output *o; 142753a5a1b3Sopenharmony_ci 142853a5a1b3Sopenharmony_ci pa_assert(c); 142953a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 143053a5a1b3Sopenharmony_ci pa_assert(u); 143153a5a1b3Sopenharmony_ci 143253a5a1b3Sopenharmony_ci if (!(o = find_output(u, s))) 143353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 143453a5a1b3Sopenharmony_ci 143553a5a1b3Sopenharmony_ci pa_log_info("Unconfiguring sink: %s", s->name); 143653a5a1b3Sopenharmony_ci 143753a5a1b3Sopenharmony_ci if (!u->automatic) 143853a5a1b3Sopenharmony_ci u->unlinked_slaves = pa_strlist_prepend(u->unlinked_slaves, s->name); 143953a5a1b3Sopenharmony_ci 144053a5a1b3Sopenharmony_ci pa_idxset_remove_by_data(u->outputs, o, NULL); 144153a5a1b3Sopenharmony_ci output_free(o); 144253a5a1b3Sopenharmony_ci 144353a5a1b3Sopenharmony_ci return PA_HOOK_OK; 144453a5a1b3Sopenharmony_ci} 144553a5a1b3Sopenharmony_ci 144653a5a1b3Sopenharmony_ci/* Called from main context */ 144753a5a1b3Sopenharmony_cistatic pa_hook_result_t sink_state_changed_hook_cb(pa_core *c, pa_sink *s, struct userdata* u) { 144853a5a1b3Sopenharmony_ci struct output *o; 144953a5a1b3Sopenharmony_ci 145053a5a1b3Sopenharmony_ci if (!(o = find_output(u, s))) 145153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 145253a5a1b3Sopenharmony_ci 145353a5a1b3Sopenharmony_ci /* This state change might be triggered because we are creating a 145453a5a1b3Sopenharmony_ci * stream here, in that case we don't want to create it a second 145553a5a1b3Sopenharmony_ci * time here and enter a loop */ 145653a5a1b3Sopenharmony_ci if (o->ignore_state_change) 145753a5a1b3Sopenharmony_ci return PA_HOOK_OK; 145853a5a1b3Sopenharmony_ci 145953a5a1b3Sopenharmony_ci output_verify(o); 146053a5a1b3Sopenharmony_ci 146153a5a1b3Sopenharmony_ci return PA_HOOK_OK; 146253a5a1b3Sopenharmony_ci} 146353a5a1b3Sopenharmony_ci 146453a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 146553a5a1b3Sopenharmony_ci struct userdata *u; 146653a5a1b3Sopenharmony_ci pa_modargs *ma = NULL; 146753a5a1b3Sopenharmony_ci const char *slaves, *rm; 146853a5a1b3Sopenharmony_ci int resample_method; 146953a5a1b3Sopenharmony_ci pa_sample_spec ss; 147053a5a1b3Sopenharmony_ci pa_channel_map map; 147153a5a1b3Sopenharmony_ci struct output *o; 147253a5a1b3Sopenharmony_ci uint32_t idx; 147353a5a1b3Sopenharmony_ci pa_sink_new_data data; 147453a5a1b3Sopenharmony_ci uint32_t adjust_time_sec; 147553a5a1b3Sopenharmony_ci size_t nbytes; 147653a5a1b3Sopenharmony_ci 147753a5a1b3Sopenharmony_ci pa_assert(m); 147853a5a1b3Sopenharmony_ci 147953a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 148053a5a1b3Sopenharmony_ci pa_log("failed to parse module arguments"); 148153a5a1b3Sopenharmony_ci goto fail; 148253a5a1b3Sopenharmony_ci } 148353a5a1b3Sopenharmony_ci 148453a5a1b3Sopenharmony_ci resample_method = m->core->resample_method; 148553a5a1b3Sopenharmony_ci if ((rm = pa_modargs_get_value(ma, "resample_method", NULL))) { 148653a5a1b3Sopenharmony_ci if ((resample_method = pa_parse_resample_method(rm)) < 0) { 148753a5a1b3Sopenharmony_ci pa_log("invalid resample method '%s'", rm); 148853a5a1b3Sopenharmony_ci goto fail; 148953a5a1b3Sopenharmony_ci } 149053a5a1b3Sopenharmony_ci } 149153a5a1b3Sopenharmony_ci 149253a5a1b3Sopenharmony_ci m->userdata = u = pa_xnew0(struct userdata, 1); 149353a5a1b3Sopenharmony_ci u->core = m->core; 149453a5a1b3Sopenharmony_ci u->module = m; 149553a5a1b3Sopenharmony_ci u->rtpoll = pa_rtpoll_new(); 149653a5a1b3Sopenharmony_ci 149753a5a1b3Sopenharmony_ci if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) { 149853a5a1b3Sopenharmony_ci pa_log("pa_thread_mq_init() failed."); 149953a5a1b3Sopenharmony_ci goto fail; 150053a5a1b3Sopenharmony_ci } 150153a5a1b3Sopenharmony_ci 150253a5a1b3Sopenharmony_ci u->remix = !m->core->disable_remixing; 150353a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "remix", &u->remix) < 0) { 150453a5a1b3Sopenharmony_ci pa_log("Invalid boolean remix parameter"); 150553a5a1b3Sopenharmony_ci goto fail; 150653a5a1b3Sopenharmony_ci } 150753a5a1b3Sopenharmony_ci 150853a5a1b3Sopenharmony_ci u->resample_method = resample_method; 150953a5a1b3Sopenharmony_ci u->outputs = pa_idxset_new(NULL, NULL); 151053a5a1b3Sopenharmony_ci#ifndef USE_SMOOTHER_2 151153a5a1b3Sopenharmony_ci u->thread_info.smoother = pa_smoother_new( 151253a5a1b3Sopenharmony_ci PA_USEC_PER_SEC, 151353a5a1b3Sopenharmony_ci PA_USEC_PER_SEC*2, 151453a5a1b3Sopenharmony_ci true, 151553a5a1b3Sopenharmony_ci true, 151653a5a1b3Sopenharmony_ci 10, 151753a5a1b3Sopenharmony_ci pa_rtclock_now(), 151853a5a1b3Sopenharmony_ci true); 151953a5a1b3Sopenharmony_ci#endif 152053a5a1b3Sopenharmony_ci 152153a5a1b3Sopenharmony_ci adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; 152253a5a1b3Sopenharmony_ci if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) { 152353a5a1b3Sopenharmony_ci pa_log("Failed to parse adjust_time value"); 152453a5a1b3Sopenharmony_ci goto fail; 152553a5a1b3Sopenharmony_ci } 152653a5a1b3Sopenharmony_ci 152753a5a1b3Sopenharmony_ci if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC) 152853a5a1b3Sopenharmony_ci u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC; 152953a5a1b3Sopenharmony_ci else 153053a5a1b3Sopenharmony_ci u->adjust_time = DEFAULT_ADJUST_TIME_USEC; 153153a5a1b3Sopenharmony_ci 153253a5a1b3Sopenharmony_ci slaves = pa_modargs_get_value(ma, "slaves", NULL); 153353a5a1b3Sopenharmony_ci u->automatic = !slaves; 153453a5a1b3Sopenharmony_ci 153553a5a1b3Sopenharmony_ci ss = m->core->default_sample_spec; 153653a5a1b3Sopenharmony_ci map = m->core->default_channel_map; 153753a5a1b3Sopenharmony_ci 153853a5a1b3Sopenharmony_ci /* Check the specified slave sinks for sample_spec and channel_map to use for the combined sink */ 153953a5a1b3Sopenharmony_ci if (!u->automatic) { 154053a5a1b3Sopenharmony_ci const char*split_state = NULL; 154153a5a1b3Sopenharmony_ci char *n = NULL; 154253a5a1b3Sopenharmony_ci pa_sample_spec slaves_spec; 154353a5a1b3Sopenharmony_ci pa_channel_map slaves_map; 154453a5a1b3Sopenharmony_ci bool is_first_slave = true; 154553a5a1b3Sopenharmony_ci 154653a5a1b3Sopenharmony_ci pa_sample_spec_init(&slaves_spec); 154753a5a1b3Sopenharmony_ci 154853a5a1b3Sopenharmony_ci while ((n = pa_split(slaves, ",", &split_state))) { 154953a5a1b3Sopenharmony_ci pa_sink *slave_sink; 155053a5a1b3Sopenharmony_ci 155153a5a1b3Sopenharmony_ci if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) { 155253a5a1b3Sopenharmony_ci pa_log("Invalid slave sink '%s'", n); 155353a5a1b3Sopenharmony_ci pa_xfree(n); 155453a5a1b3Sopenharmony_ci goto fail; 155553a5a1b3Sopenharmony_ci } 155653a5a1b3Sopenharmony_ci 155753a5a1b3Sopenharmony_ci pa_xfree(n); 155853a5a1b3Sopenharmony_ci 155953a5a1b3Sopenharmony_ci if (is_first_slave) { 156053a5a1b3Sopenharmony_ci slaves_spec = slave_sink->sample_spec; 156153a5a1b3Sopenharmony_ci slaves_map = slave_sink->channel_map; 156253a5a1b3Sopenharmony_ci is_first_slave = false; 156353a5a1b3Sopenharmony_ci } else { 156453a5a1b3Sopenharmony_ci if (slaves_spec.format != slave_sink->sample_spec.format) 156553a5a1b3Sopenharmony_ci slaves_spec.format = PA_SAMPLE_INVALID; 156653a5a1b3Sopenharmony_ci 156753a5a1b3Sopenharmony_ci if (slaves_spec.rate < slave_sink->sample_spec.rate) 156853a5a1b3Sopenharmony_ci slaves_spec.rate = slave_sink->sample_spec.rate; 156953a5a1b3Sopenharmony_ci 157053a5a1b3Sopenharmony_ci if (!pa_channel_map_equal(&slaves_map, &slave_sink->channel_map)) 157153a5a1b3Sopenharmony_ci slaves_spec.channels = 0; 157253a5a1b3Sopenharmony_ci } 157353a5a1b3Sopenharmony_ci } 157453a5a1b3Sopenharmony_ci 157553a5a1b3Sopenharmony_ci if (!is_first_slave) { 157653a5a1b3Sopenharmony_ci if (slaves_spec.format != PA_SAMPLE_INVALID) 157753a5a1b3Sopenharmony_ci ss.format = slaves_spec.format; 157853a5a1b3Sopenharmony_ci 157953a5a1b3Sopenharmony_ci ss.rate = slaves_spec.rate; 158053a5a1b3Sopenharmony_ci 158153a5a1b3Sopenharmony_ci if (slaves_spec.channels > 0) { 158253a5a1b3Sopenharmony_ci map = slaves_map; 158353a5a1b3Sopenharmony_ci ss.channels = slaves_map.channels; 158453a5a1b3Sopenharmony_ci } 158553a5a1b3Sopenharmony_ci } 158653a5a1b3Sopenharmony_ci } 158753a5a1b3Sopenharmony_ci 158853a5a1b3Sopenharmony_ci if ((pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0)) { 158953a5a1b3Sopenharmony_ci pa_log("Invalid sample specification."); 159053a5a1b3Sopenharmony_ci goto fail; 159153a5a1b3Sopenharmony_ci } 159253a5a1b3Sopenharmony_ci 159353a5a1b3Sopenharmony_ci pa_sink_new_data_init(&data); 159453a5a1b3Sopenharmony_ci data.namereg_fail = false; 159553a5a1b3Sopenharmony_ci data.driver = __FILE__; 159653a5a1b3Sopenharmony_ci data.module = m; 159753a5a1b3Sopenharmony_ci pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); 159853a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&data, &ss); 159953a5a1b3Sopenharmony_ci pa_sink_new_data_set_channel_map(&data, &map); 160053a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "filter"); 160153a5a1b3Sopenharmony_ci 160253a5a1b3Sopenharmony_ci if (slaves) 160353a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, "combine.slaves", slaves); 160453a5a1b3Sopenharmony_ci 160553a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { 160653a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 160753a5a1b3Sopenharmony_ci pa_sink_new_data_done(&data); 160853a5a1b3Sopenharmony_ci goto fail; 160953a5a1b3Sopenharmony_ci } 161053a5a1b3Sopenharmony_ci 161153a5a1b3Sopenharmony_ci /* Check proplist for a description & fill in a default value if not */ 161253a5a1b3Sopenharmony_ci u->auto_desc = false; 161353a5a1b3Sopenharmony_ci if (NULL == pa_proplist_gets(data.proplist, PA_PROP_DEVICE_DESCRIPTION)) { 161453a5a1b3Sopenharmony_ci u->auto_desc = true; 161553a5a1b3Sopenharmony_ci pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Simultaneous Output"); 161653a5a1b3Sopenharmony_ci } 161753a5a1b3Sopenharmony_ci 161853a5a1b3Sopenharmony_ci u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY); 161953a5a1b3Sopenharmony_ci pa_sink_new_data_done(&data); 162053a5a1b3Sopenharmony_ci 162153a5a1b3Sopenharmony_ci if (!u->sink) { 162253a5a1b3Sopenharmony_ci pa_log("Failed to create sink"); 162353a5a1b3Sopenharmony_ci goto fail; 162453a5a1b3Sopenharmony_ci } 162553a5a1b3Sopenharmony_ci 162653a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 162753a5a1b3Sopenharmony_ci /* The smoother window size needs to be larger than the time between updates */ 162853a5a1b3Sopenharmony_ci u->thread_info.smoother = pa_smoother_2_new(u->adjust_time + 5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->sink->sample_spec), u->sink->sample_spec.rate); 162953a5a1b3Sopenharmony_ci#endif 163053a5a1b3Sopenharmony_ci 163153a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg; 163253a5a1b3Sopenharmony_ci u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb; 163353a5a1b3Sopenharmony_ci u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; 163453a5a1b3Sopenharmony_ci u->sink->update_requested_latency = sink_update_requested_latency; 163553a5a1b3Sopenharmony_ci u->sink->userdata = u; 163653a5a1b3Sopenharmony_ci 163753a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, u->rtpoll); 163853a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); 163953a5a1b3Sopenharmony_ci 164053a5a1b3Sopenharmony_ci nbytes = pa_usec_to_bytes(BLOCK_USEC, &u->sink->sample_spec); 164153a5a1b3Sopenharmony_ci pa_sink_set_max_request(u->sink, nbytes); 164253a5a1b3Sopenharmony_ci pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC); 164353a5a1b3Sopenharmony_ci /* pulse clamps the range, get the real values */ 164453a5a1b3Sopenharmony_ci u->default_min_latency = u->sink->thread_info.min_latency; 164553a5a1b3Sopenharmony_ci u->default_max_latency = u->sink->thread_info.max_latency; 164653a5a1b3Sopenharmony_ci u->block_usec = u->sink->thread_info.max_latency; 164753a5a1b3Sopenharmony_ci 164853a5a1b3Sopenharmony_ci 164953a5a1b3Sopenharmony_ci if (!u->automatic) { 165053a5a1b3Sopenharmony_ci const char*split_state; 165153a5a1b3Sopenharmony_ci char *n = NULL; 165253a5a1b3Sopenharmony_ci pa_assert(slaves); 165353a5a1b3Sopenharmony_ci 165453a5a1b3Sopenharmony_ci /* The slaves have been specified manually */ 165553a5a1b3Sopenharmony_ci 165653a5a1b3Sopenharmony_ci split_state = NULL; 165753a5a1b3Sopenharmony_ci while ((n = pa_split(slaves, ",", &split_state))) { 165853a5a1b3Sopenharmony_ci pa_sink *slave_sink; 165953a5a1b3Sopenharmony_ci 166053a5a1b3Sopenharmony_ci if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK)) || slave_sink == u->sink) { 166153a5a1b3Sopenharmony_ci pa_log("Invalid slave sink '%s'", n); 166253a5a1b3Sopenharmony_ci pa_xfree(n); 166353a5a1b3Sopenharmony_ci goto fail; 166453a5a1b3Sopenharmony_ci } 166553a5a1b3Sopenharmony_ci 166653a5a1b3Sopenharmony_ci pa_xfree(n); 166753a5a1b3Sopenharmony_ci 166853a5a1b3Sopenharmony_ci if (!output_new(u, slave_sink)) { 166953a5a1b3Sopenharmony_ci pa_log("Failed to create slave sink input on sink '%s'.", slave_sink->name); 167053a5a1b3Sopenharmony_ci goto fail; 167153a5a1b3Sopenharmony_ci } 167253a5a1b3Sopenharmony_ci } 167353a5a1b3Sopenharmony_ci 167453a5a1b3Sopenharmony_ci if (pa_idxset_size(u->outputs) <= 1) 167553a5a1b3Sopenharmony_ci pa_log_warn("No slave sinks specified."); 167653a5a1b3Sopenharmony_ci 167753a5a1b3Sopenharmony_ci u->sink_put_slot = NULL; 167853a5a1b3Sopenharmony_ci 167953a5a1b3Sopenharmony_ci } else { 168053a5a1b3Sopenharmony_ci pa_sink *s; 168153a5a1b3Sopenharmony_ci 168253a5a1b3Sopenharmony_ci /* We're in automatic mode, we add every sink that matches our needs */ 168353a5a1b3Sopenharmony_ci 168453a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(s, m->core->sinks, idx) { 168553a5a1b3Sopenharmony_ci 168653a5a1b3Sopenharmony_ci if (!is_suitable_sink(u, s)) 168753a5a1b3Sopenharmony_ci continue; 168853a5a1b3Sopenharmony_ci 168953a5a1b3Sopenharmony_ci if (!output_new(u, s)) { 169053a5a1b3Sopenharmony_ci pa_log("Failed to create sink input on sink '%s'.", s->name); 169153a5a1b3Sopenharmony_ci goto fail; 169253a5a1b3Sopenharmony_ci } 169353a5a1b3Sopenharmony_ci } 169453a5a1b3Sopenharmony_ci } 169553a5a1b3Sopenharmony_ci 169653a5a1b3Sopenharmony_ci u->sink_put_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_cb, u); 169753a5a1b3Sopenharmony_ci u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_EARLY, (pa_hook_cb_t) sink_unlink_hook_cb, u); 169853a5a1b3Sopenharmony_ci u->sink_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_state_changed_hook_cb, u); 169953a5a1b3Sopenharmony_ci 170053a5a1b3Sopenharmony_ci u->thread_info.render_timestamp = 0; 170153a5a1b3Sopenharmony_ci 170253a5a1b3Sopenharmony_ci if (!(u->thread = pa_thread_new("combine", thread_func, u))) { 170353a5a1b3Sopenharmony_ci pa_log("Failed to create thread."); 170453a5a1b3Sopenharmony_ci goto fail; 170553a5a1b3Sopenharmony_ci } 170653a5a1b3Sopenharmony_ci 170753a5a1b3Sopenharmony_ci /* Activate the sink and the sink inputs */ 170853a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 170953a5a1b3Sopenharmony_ci 171053a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(o, u->outputs, idx) 171153a5a1b3Sopenharmony_ci output_verify(o); 171253a5a1b3Sopenharmony_ci 171353a5a1b3Sopenharmony_ci if (u->adjust_time > 0) 171453a5a1b3Sopenharmony_ci u->time_event = pa_core_rttime_new(m->core, pa_rtclock_now() + u->adjust_time, time_callback, u); 171553a5a1b3Sopenharmony_ci 171653a5a1b3Sopenharmony_ci pa_modargs_free(ma); 171753a5a1b3Sopenharmony_ci 171853a5a1b3Sopenharmony_ci return 0; 171953a5a1b3Sopenharmony_ci 172053a5a1b3Sopenharmony_cifail: 172153a5a1b3Sopenharmony_ci 172253a5a1b3Sopenharmony_ci if (ma) 172353a5a1b3Sopenharmony_ci pa_modargs_free(ma); 172453a5a1b3Sopenharmony_ci 172553a5a1b3Sopenharmony_ci pa__done(m); 172653a5a1b3Sopenharmony_ci 172753a5a1b3Sopenharmony_ci return -1; 172853a5a1b3Sopenharmony_ci} 172953a5a1b3Sopenharmony_ci 173053a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 173153a5a1b3Sopenharmony_ci struct userdata *u; 173253a5a1b3Sopenharmony_ci 173353a5a1b3Sopenharmony_ci pa_assert(m); 173453a5a1b3Sopenharmony_ci 173553a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 173653a5a1b3Sopenharmony_ci return; 173753a5a1b3Sopenharmony_ci 173853a5a1b3Sopenharmony_ci pa_strlist_free(u->unlinked_slaves); 173953a5a1b3Sopenharmony_ci 174053a5a1b3Sopenharmony_ci if (u->sink_put_slot) 174153a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_put_slot); 174253a5a1b3Sopenharmony_ci 174353a5a1b3Sopenharmony_ci if (u->sink_unlink_slot) 174453a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_unlink_slot); 174553a5a1b3Sopenharmony_ci 174653a5a1b3Sopenharmony_ci if (u->sink_state_changed_slot) 174753a5a1b3Sopenharmony_ci pa_hook_slot_free(u->sink_state_changed_slot); 174853a5a1b3Sopenharmony_ci 174953a5a1b3Sopenharmony_ci if (u->outputs) 175053a5a1b3Sopenharmony_ci pa_idxset_free(u->outputs, (pa_free_cb_t) output_free); 175153a5a1b3Sopenharmony_ci 175253a5a1b3Sopenharmony_ci if (u->sink) 175353a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 175453a5a1b3Sopenharmony_ci 175553a5a1b3Sopenharmony_ci if (u->thread) { 175653a5a1b3Sopenharmony_ci pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); 175753a5a1b3Sopenharmony_ci pa_thread_free(u->thread); 175853a5a1b3Sopenharmony_ci } 175953a5a1b3Sopenharmony_ci 176053a5a1b3Sopenharmony_ci pa_thread_mq_done(&u->thread_mq); 176153a5a1b3Sopenharmony_ci 176253a5a1b3Sopenharmony_ci if (u->sink) 176353a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 176453a5a1b3Sopenharmony_ci 176553a5a1b3Sopenharmony_ci if (u->rtpoll) 176653a5a1b3Sopenharmony_ci pa_rtpoll_free(u->rtpoll); 176753a5a1b3Sopenharmony_ci 176853a5a1b3Sopenharmony_ci if (u->time_event) 176953a5a1b3Sopenharmony_ci u->core->mainloop->time_free(u->time_event); 177053a5a1b3Sopenharmony_ci 177153a5a1b3Sopenharmony_ci if (u->thread_info.smoother) 177253a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2 177353a5a1b3Sopenharmony_ci pa_smoother_2_free(u->thread_info.smoother); 177453a5a1b3Sopenharmony_ci#else 177553a5a1b3Sopenharmony_ci pa_smoother_free(u->thread_info.smoother); 177653a5a1b3Sopenharmony_ci#endif 177753a5a1b3Sopenharmony_ci 177853a5a1b3Sopenharmony_ci pa_xfree(u); 177953a5a1b3Sopenharmony_ci} 1780