153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2010 Intel Corporation 553a5a1b3Sopenharmony_ci Contributor: Pierre-Louis Bossart <pierre-louis.bossart@intel.com> 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 953a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1053a5a1b3Sopenharmony_ci or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1853a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <pulse/gccmacro.h> 2653a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2753a5a1b3Sopenharmony_ci 2853a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 3153a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3253a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3353a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 3453a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/ltdl-helper.h> 3853a5a1b3Sopenharmony_ci 3953a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Pierre-Louis Bossart"); 4053a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION(_("Virtual sink")); 4153a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 4253a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 4353a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 4453a5a1b3Sopenharmony_ci _("sink_name=<name for the sink> " 4553a5a1b3Sopenharmony_ci "sink_properties=<properties for the sink> " 4653a5a1b3Sopenharmony_ci "master=<name of sink to filter> " 4753a5a1b3Sopenharmony_ci "rate=<sample rate> " 4853a5a1b3Sopenharmony_ci "channels=<number of channels> " 4953a5a1b3Sopenharmony_ci "channel_map=<channel map> " 5053a5a1b3Sopenharmony_ci "use_volume_sharing=<yes or no> " 5153a5a1b3Sopenharmony_ci "force_flat_volume=<yes or no> " 5253a5a1b3Sopenharmony_ci )); 5353a5a1b3Sopenharmony_ci 5453a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) 5553a5a1b3Sopenharmony_ci 5653a5a1b3Sopenharmony_cistruct userdata { 5753a5a1b3Sopenharmony_ci pa_module *module; 5853a5a1b3Sopenharmony_ci 5953a5a1b3Sopenharmony_ci /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */ 6053a5a1b3Sopenharmony_ci /* bool autoloaded; */ 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_ci pa_sink *sink; 6353a5a1b3Sopenharmony_ci pa_sink_input *sink_input; 6453a5a1b3Sopenharmony_ci 6553a5a1b3Sopenharmony_ci pa_memblockq *memblockq; 6653a5a1b3Sopenharmony_ci 6753a5a1b3Sopenharmony_ci bool auto_desc; 6853a5a1b3Sopenharmony_ci unsigned channels; 6953a5a1b3Sopenharmony_ci}; 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 7253a5a1b3Sopenharmony_ci "sink_name", 7353a5a1b3Sopenharmony_ci "sink_properties", 7453a5a1b3Sopenharmony_ci "master", 7553a5a1b3Sopenharmony_ci "rate", 7653a5a1b3Sopenharmony_ci "channels", 7753a5a1b3Sopenharmony_ci "channel_map", 7853a5a1b3Sopenharmony_ci "use_volume_sharing", 7953a5a1b3Sopenharmony_ci "force_flat_volume", 8053a5a1b3Sopenharmony_ci NULL 8153a5a1b3Sopenharmony_ci}; 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 8453a5a1b3Sopenharmony_cistatic int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 8553a5a1b3Sopenharmony_ci struct userdata *u = PA_SINK(o)->userdata; 8653a5a1b3Sopenharmony_ci 8753a5a1b3Sopenharmony_ci switch (code) { 8853a5a1b3Sopenharmony_ci 8953a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_ci /* The sink is _put() before the sink input is, so let's 9253a5a1b3Sopenharmony_ci * make sure we don't access it in that time. Also, the 9353a5a1b3Sopenharmony_ci * sink input is first shut down, the sink second. */ 9453a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 9553a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) { 9653a5a1b3Sopenharmony_ci *((int64_t*) data) = 0; 9753a5a1b3Sopenharmony_ci return 0; 9853a5a1b3Sopenharmony_ci } 9953a5a1b3Sopenharmony_ci 10053a5a1b3Sopenharmony_ci *((int64_t*) data) = 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_ci /* Get the latency of the master sink */ 10353a5a1b3Sopenharmony_ci pa_sink_get_latency_within_thread(u->sink_input->sink, true) + 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_ci /* Add the latency internal to our sink input on top */ 10653a5a1b3Sopenharmony_ci pa_bytes_to_usec(pa_memblockq_get_length(u->sink_input->thread_info.render_memblockq), &u->sink_input->sink->sample_spec); 10753a5a1b3Sopenharmony_ci 10853a5a1b3Sopenharmony_ci /* Add resampler latency */ 10953a5a1b3Sopenharmony_ci *((int64_t*) data) += pa_resampler_get_delay_usec(u->sink_input->thread_info.resampler); 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_ci return 0; 11253a5a1b3Sopenharmony_ci } 11353a5a1b3Sopenharmony_ci 11453a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 11553a5a1b3Sopenharmony_ci} 11653a5a1b3Sopenharmony_ci 11753a5a1b3Sopenharmony_ci/* Called from main context */ 11853a5a1b3Sopenharmony_cistatic int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) { 11953a5a1b3Sopenharmony_ci struct userdata *u; 12053a5a1b3Sopenharmony_ci 12153a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 12253a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(state) || 12553a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 12653a5a1b3Sopenharmony_ci return 0; 12753a5a1b3Sopenharmony_ci 12853a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, state == PA_SINK_SUSPENDED); 12953a5a1b3Sopenharmony_ci return 0; 13053a5a1b3Sopenharmony_ci} 13153a5a1b3Sopenharmony_ci 13253a5a1b3Sopenharmony_ci/* Called from the IO thread. */ 13353a5a1b3Sopenharmony_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) { 13453a5a1b3Sopenharmony_ci struct userdata *u; 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci pa_assert(s); 13753a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 13853a5a1b3Sopenharmony_ci 13953a5a1b3Sopenharmony_ci /* When set to running or idle for the first time, request a rewind 14053a5a1b3Sopenharmony_ci * of the master sink to make sure we are heard immediately */ 14153a5a1b3Sopenharmony_ci if (PA_SINK_IS_OPENED(new_state) && s->thread_info.state == PA_SINK_INIT) { 14253a5a1b3Sopenharmony_ci pa_log_debug("Requesting rewind due to state change."); 14353a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(u->sink_input, 0, false, true, true); 14453a5a1b3Sopenharmony_ci } 14553a5a1b3Sopenharmony_ci 14653a5a1b3Sopenharmony_ci return 0; 14753a5a1b3Sopenharmony_ci} 14853a5a1b3Sopenharmony_ci 14953a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 15053a5a1b3Sopenharmony_cistatic void sink_request_rewind_cb(pa_sink *s) { 15153a5a1b3Sopenharmony_ci struct userdata *u; 15253a5a1b3Sopenharmony_ci 15353a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 15453a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 15753a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) 15853a5a1b3Sopenharmony_ci return; 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci /* Just hand this one over to the master sink */ 16153a5a1b3Sopenharmony_ci pa_sink_input_request_rewind(u->sink_input, 16253a5a1b3Sopenharmony_ci s->thread_info.rewind_nbytes + 16353a5a1b3Sopenharmony_ci pa_memblockq_get_length(u->memblockq), true, false, false); 16453a5a1b3Sopenharmony_ci} 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 16753a5a1b3Sopenharmony_cistatic void sink_update_requested_latency_cb(pa_sink *s) { 16853a5a1b3Sopenharmony_ci struct userdata *u; 16953a5a1b3Sopenharmony_ci 17053a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 17153a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state) || 17453a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->thread_info.state)) 17553a5a1b3Sopenharmony_ci return; 17653a5a1b3Sopenharmony_ci 17753a5a1b3Sopenharmony_ci /* Just hand this one over to the master sink */ 17853a5a1b3Sopenharmony_ci pa_sink_input_set_requested_latency_within_thread( 17953a5a1b3Sopenharmony_ci u->sink_input, 18053a5a1b3Sopenharmony_ci pa_sink_get_requested_latency_within_thread(s)); 18153a5a1b3Sopenharmony_ci} 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci/* Called from main context */ 18453a5a1b3Sopenharmony_cistatic void sink_set_volume_cb(pa_sink *s) { 18553a5a1b3Sopenharmony_ci struct userdata *u; 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 18853a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 18953a5a1b3Sopenharmony_ci 19053a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(s->state) || 19153a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 19253a5a1b3Sopenharmony_ci return; 19353a5a1b3Sopenharmony_ci 19453a5a1b3Sopenharmony_ci pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, true); 19553a5a1b3Sopenharmony_ci} 19653a5a1b3Sopenharmony_ci 19753a5a1b3Sopenharmony_ci/* Called from main context */ 19853a5a1b3Sopenharmony_cistatic void sink_set_mute_cb(pa_sink *s) { 19953a5a1b3Sopenharmony_ci struct userdata *u; 20053a5a1b3Sopenharmony_ci 20153a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 20253a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 20353a5a1b3Sopenharmony_ci 20453a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(s->state) || 20553a5a1b3Sopenharmony_ci !PA_SINK_INPUT_IS_LINKED(u->sink_input->state)) 20653a5a1b3Sopenharmony_ci return; 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); 20953a5a1b3Sopenharmony_ci} 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 21253a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { 21353a5a1b3Sopenharmony_ci struct userdata *u; 21453a5a1b3Sopenharmony_ci float *src, *dst; 21553a5a1b3Sopenharmony_ci size_t fs; 21653a5a1b3Sopenharmony_ci unsigned n, c; 21753a5a1b3Sopenharmony_ci pa_memchunk tchunk; 21853a5a1b3Sopenharmony_ci pa_usec_t current_latency PA_GCC_UNUSED; 21953a5a1b3Sopenharmony_ci 22053a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 22153a5a1b3Sopenharmony_ci pa_assert(chunk); 22253a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 22353a5a1b3Sopenharmony_ci 22453a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state)) 22553a5a1b3Sopenharmony_ci return -1; 22653a5a1b3Sopenharmony_ci 22753a5a1b3Sopenharmony_ci /* Hmm, process any rewind request that might be queued up */ 22853a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 22953a5a1b3Sopenharmony_ci 23053a5a1b3Sopenharmony_ci /* (1) IF YOU NEED A FIXED BLOCK SIZE USE 23153a5a1b3Sopenharmony_ci * pa_memblockq_peek_fixed_size() HERE INSTEAD. NOTE THAT FILTERS 23253a5a1b3Sopenharmony_ci * WHICH CAN DEAL WITH DYNAMIC BLOCK SIZES ARE HIGHLY 23353a5a1b3Sopenharmony_ci * PREFERRED. */ 23453a5a1b3Sopenharmony_ci while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { 23553a5a1b3Sopenharmony_ci pa_memchunk nchunk; 23653a5a1b3Sopenharmony_ci 23753a5a1b3Sopenharmony_ci pa_sink_render(u->sink, nbytes, &nchunk); 23853a5a1b3Sopenharmony_ci pa_memblockq_push(u->memblockq, &nchunk); 23953a5a1b3Sopenharmony_ci pa_memblock_unref(nchunk.memblock); 24053a5a1b3Sopenharmony_ci } 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci /* (2) IF YOU NEED A FIXED BLOCK SIZE, THIS NEXT LINE IS NOT 24353a5a1b3Sopenharmony_ci * NECESSARY */ 24453a5a1b3Sopenharmony_ci tchunk.length = PA_MIN(nbytes, tchunk.length); 24553a5a1b3Sopenharmony_ci pa_assert(tchunk.length > 0); 24653a5a1b3Sopenharmony_ci 24753a5a1b3Sopenharmony_ci fs = pa_frame_size(&i->sample_spec); 24853a5a1b3Sopenharmony_ci n = (unsigned) (tchunk.length / fs); 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci pa_assert(n > 0); 25153a5a1b3Sopenharmony_ci 25253a5a1b3Sopenharmony_ci chunk->index = 0; 25353a5a1b3Sopenharmony_ci chunk->length = n*fs; 25453a5a1b3Sopenharmony_ci chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); 25553a5a1b3Sopenharmony_ci 25653a5a1b3Sopenharmony_ci pa_memblockq_drop(u->memblockq, chunk->length); 25753a5a1b3Sopenharmony_ci 25853a5a1b3Sopenharmony_ci src = pa_memblock_acquire_chunk(&tchunk); 25953a5a1b3Sopenharmony_ci dst = pa_memblock_acquire(chunk->memblock); 26053a5a1b3Sopenharmony_ci 26153a5a1b3Sopenharmony_ci /* (3) PUT YOUR CODE HERE TO DO SOMETHING WITH THE DATA */ 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci /* As an example, copy input to output */ 26453a5a1b3Sopenharmony_ci for (c = 0; c < u->channels; c++) { 26553a5a1b3Sopenharmony_ci pa_sample_clamp(PA_SAMPLE_FLOAT32NE, 26653a5a1b3Sopenharmony_ci dst+c, u->channels * sizeof(float), 26753a5a1b3Sopenharmony_ci src+c, u->channels * sizeof(float), 26853a5a1b3Sopenharmony_ci n); 26953a5a1b3Sopenharmony_ci } 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci pa_memblock_release(tchunk.memblock); 27253a5a1b3Sopenharmony_ci pa_memblock_release(chunk->memblock); 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_ci pa_memblock_unref(tchunk.memblock); 27553a5a1b3Sopenharmony_ci 27653a5a1b3Sopenharmony_ci /* (4) IF YOU NEED THE LATENCY FOR SOMETHING ACQUIRE IT LIKE THIS: */ 27753a5a1b3Sopenharmony_ci current_latency = 27853a5a1b3Sopenharmony_ci /* Get the latency of the master sink */ 27953a5a1b3Sopenharmony_ci pa_sink_get_latency_within_thread(i->sink, false) + 28053a5a1b3Sopenharmony_ci 28153a5a1b3Sopenharmony_ci /* Add the latency internal to our sink input on top */ 28253a5a1b3Sopenharmony_ci pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); 28353a5a1b3Sopenharmony_ci 28453a5a1b3Sopenharmony_ci return 0; 28553a5a1b3Sopenharmony_ci} 28653a5a1b3Sopenharmony_ci 28753a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 28853a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { 28953a5a1b3Sopenharmony_ci struct userdata *u; 29053a5a1b3Sopenharmony_ci size_t amount = 0; 29153a5a1b3Sopenharmony_ci 29253a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 29353a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 29453a5a1b3Sopenharmony_ci 29553a5a1b3Sopenharmony_ci /* If the sink is not yet linked, there is nothing to rewind */ 29653a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(u->sink->thread_info.state)) 29753a5a1b3Sopenharmony_ci return; 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ci if (u->sink->thread_info.rewind_nbytes > 0) { 30053a5a1b3Sopenharmony_ci size_t max_rewrite; 30153a5a1b3Sopenharmony_ci 30253a5a1b3Sopenharmony_ci max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); 30353a5a1b3Sopenharmony_ci amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); 30453a5a1b3Sopenharmony_ci u->sink->thread_info.rewind_nbytes = 0; 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_ci if (amount > 0) { 30753a5a1b3Sopenharmony_ci pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true); 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_ci /* (5) PUT YOUR CODE HERE TO RESET YOUR FILTER */ 31053a5a1b3Sopenharmony_ci } 31153a5a1b3Sopenharmony_ci } 31253a5a1b3Sopenharmony_ci 31353a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, amount); 31453a5a1b3Sopenharmony_ci pa_memblockq_rewind(u->memblockq, nbytes); 31553a5a1b3Sopenharmony_ci} 31653a5a1b3Sopenharmony_ci 31753a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 31853a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) { 31953a5a1b3Sopenharmony_ci struct userdata *u; 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 32253a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 32353a5a1b3Sopenharmony_ci 32453a5a1b3Sopenharmony_ci /* FIXME: Too small max_rewind: 32553a5a1b3Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */ 32653a5a1b3Sopenharmony_ci pa_memblockq_set_maxrewind(u->memblockq, nbytes); 32753a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(u->sink, nbytes); 32853a5a1b3Sopenharmony_ci} 32953a5a1b3Sopenharmony_ci 33053a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 33153a5a1b3Sopenharmony_cistatic void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) { 33253a5a1b3Sopenharmony_ci struct userdata *u; 33353a5a1b3Sopenharmony_ci 33453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 33553a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 33653a5a1b3Sopenharmony_ci 33753a5a1b3Sopenharmony_ci /* (6) IF YOU NEED A FIXED BLOCK SIZE ROUND nbytes UP TO MULTIPLES 33853a5a1b3Sopenharmony_ci * OF IT HERE. THE PA_ROUND_UP MACRO IS USEFUL FOR THAT. */ 33953a5a1b3Sopenharmony_ci 34053a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, nbytes); 34153a5a1b3Sopenharmony_ci} 34253a5a1b3Sopenharmony_ci 34353a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 34453a5a1b3Sopenharmony_cistatic void sink_input_update_sink_latency_range_cb(pa_sink_input *i) { 34553a5a1b3Sopenharmony_ci struct userdata *u; 34653a5a1b3Sopenharmony_ci 34753a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 34853a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 34953a5a1b3Sopenharmony_ci 35053a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); 35153a5a1b3Sopenharmony_ci} 35253a5a1b3Sopenharmony_ci 35353a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 35453a5a1b3Sopenharmony_cistatic void sink_input_update_sink_fixed_latency_cb(pa_sink_input *i) { 35553a5a1b3Sopenharmony_ci struct userdata *u; 35653a5a1b3Sopenharmony_ci 35753a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 35853a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci /* (7) IF YOU NEED A FIXED BLOCK SIZE ADD THE LATENCY FOR ONE 36153a5a1b3Sopenharmony_ci * BLOCK MINUS ONE SAMPLE HERE. pa_usec_to_bytes_round_up() IS 36253a5a1b3Sopenharmony_ci * USEFUL FOR THAT. */ 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); 36553a5a1b3Sopenharmony_ci} 36653a5a1b3Sopenharmony_ci 36753a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 36853a5a1b3Sopenharmony_cistatic void sink_input_detach_cb(pa_sink_input *i) { 36953a5a1b3Sopenharmony_ci struct userdata *u; 37053a5a1b3Sopenharmony_ci 37153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 37253a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 37353a5a1b3Sopenharmony_ci 37453a5a1b3Sopenharmony_ci if (PA_SINK_IS_LINKED(u->sink->thread_info.state)) 37553a5a1b3Sopenharmony_ci pa_sink_detach_within_thread(u->sink); 37653a5a1b3Sopenharmony_ci 37753a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, NULL); 37853a5a1b3Sopenharmony_ci} 37953a5a1b3Sopenharmony_ci 38053a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 38153a5a1b3Sopenharmony_cistatic void sink_input_attach_cb(pa_sink_input *i) { 38253a5a1b3Sopenharmony_ci struct userdata *u; 38353a5a1b3Sopenharmony_ci 38453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 38553a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 38653a5a1b3Sopenharmony_ci 38753a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, i->sink->thread_info.rtpoll); 38853a5a1b3Sopenharmony_ci pa_sink_set_latency_range_within_thread(u->sink, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency); 38953a5a1b3Sopenharmony_ci 39053a5a1b3Sopenharmony_ci /* (8.1) IF YOU NEED A FIXED BLOCK SIZE ADD THE LATENCY FOR ONE 39153a5a1b3Sopenharmony_ci * BLOCK MINUS ONE SAMPLE HERE. SEE (7) */ 39253a5a1b3Sopenharmony_ci pa_sink_set_fixed_latency_within_thread(u->sink, i->sink->thread_info.fixed_latency); 39353a5a1b3Sopenharmony_ci 39453a5a1b3Sopenharmony_ci /* (8.2) IF YOU NEED A FIXED BLOCK SIZE ROUND 39553a5a1b3Sopenharmony_ci * pa_sink_input_get_max_request(i) UP TO MULTIPLES OF IT 39653a5a1b3Sopenharmony_ci * HERE. SEE (6) */ 39753a5a1b3Sopenharmony_ci pa_sink_set_max_request_within_thread(u->sink, pa_sink_input_get_max_request(i)); 39853a5a1b3Sopenharmony_ci 39953a5a1b3Sopenharmony_ci /* FIXME: Too small max_rewind: 40053a5a1b3Sopenharmony_ci * https://bugs.freedesktop.org/show_bug.cgi?id=53709 */ 40153a5a1b3Sopenharmony_ci pa_sink_set_max_rewind_within_thread(u->sink, pa_sink_input_get_max_rewind(i)); 40253a5a1b3Sopenharmony_ci 40353a5a1b3Sopenharmony_ci if (PA_SINK_IS_LINKED(u->sink->thread_info.state)) 40453a5a1b3Sopenharmony_ci pa_sink_attach_within_thread(u->sink); 40553a5a1b3Sopenharmony_ci} 40653a5a1b3Sopenharmony_ci 40753a5a1b3Sopenharmony_ci/* Called from main context */ 40853a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) { 40953a5a1b3Sopenharmony_ci struct userdata *u; 41053a5a1b3Sopenharmony_ci 41153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 41253a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 41353a5a1b3Sopenharmony_ci 41453a5a1b3Sopenharmony_ci /* The order here matters! We first kill the sink so that streams 41553a5a1b3Sopenharmony_ci * can properly be moved away while the sink input is still connected 41653a5a1b3Sopenharmony_ci * to the master. */ 41753a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, true); 41853a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 41953a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 42053a5a1b3Sopenharmony_ci 42153a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 42253a5a1b3Sopenharmony_ci u->sink_input = NULL; 42353a5a1b3Sopenharmony_ci 42453a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 42553a5a1b3Sopenharmony_ci u->sink = NULL; 42653a5a1b3Sopenharmony_ci 42753a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 42853a5a1b3Sopenharmony_ci} 42953a5a1b3Sopenharmony_ci 43053a5a1b3Sopenharmony_ci/* Called from main context */ 43153a5a1b3Sopenharmony_cistatic void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { 43253a5a1b3Sopenharmony_ci struct userdata *u; 43353a5a1b3Sopenharmony_ci 43453a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 43553a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 43653a5a1b3Sopenharmony_ci 43753a5a1b3Sopenharmony_ci if (dest) { 43853a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, dest->asyncmsgq); 43953a5a1b3Sopenharmony_ci pa_sink_update_flags(u->sink, PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY, dest->flags); 44053a5a1b3Sopenharmony_ci } else 44153a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, NULL); 44253a5a1b3Sopenharmony_ci 44353a5a1b3Sopenharmony_ci if (u->auto_desc && dest) { 44453a5a1b3Sopenharmony_ci const char *z; 44553a5a1b3Sopenharmony_ci pa_proplist *pl; 44653a5a1b3Sopenharmony_ci 44753a5a1b3Sopenharmony_ci pl = pa_proplist_new(); 44853a5a1b3Sopenharmony_ci z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); 44953a5a1b3Sopenharmony_ci pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Sink %s on %s", 45053a5a1b3Sopenharmony_ci pa_proplist_gets(u->sink->proplist, "device.vsink.name"), z ? z : dest->name); 45153a5a1b3Sopenharmony_ci 45253a5a1b3Sopenharmony_ci pa_sink_update_proplist(u->sink, PA_UPDATE_REPLACE, pl); 45353a5a1b3Sopenharmony_ci pa_proplist_free(pl); 45453a5a1b3Sopenharmony_ci } 45553a5a1b3Sopenharmony_ci} 45653a5a1b3Sopenharmony_ci 45753a5a1b3Sopenharmony_ci/* Called from main context */ 45853a5a1b3Sopenharmony_cistatic void sink_input_volume_changed_cb(pa_sink_input *i) { 45953a5a1b3Sopenharmony_ci struct userdata *u; 46053a5a1b3Sopenharmony_ci 46153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 46253a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 46353a5a1b3Sopenharmony_ci 46453a5a1b3Sopenharmony_ci pa_sink_volume_changed(u->sink, &i->volume); 46553a5a1b3Sopenharmony_ci} 46653a5a1b3Sopenharmony_ci 46753a5a1b3Sopenharmony_ci/* Called from main context */ 46853a5a1b3Sopenharmony_cistatic void sink_input_mute_changed_cb(pa_sink_input *i) { 46953a5a1b3Sopenharmony_ci struct userdata *u; 47053a5a1b3Sopenharmony_ci 47153a5a1b3Sopenharmony_ci pa_sink_input_assert_ref(i); 47253a5a1b3Sopenharmony_ci pa_assert_se(u = i->userdata); 47353a5a1b3Sopenharmony_ci 47453a5a1b3Sopenharmony_ci pa_sink_mute_changed(u->sink, i->muted); 47553a5a1b3Sopenharmony_ci} 47653a5a1b3Sopenharmony_ci 47753a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 47853a5a1b3Sopenharmony_ci struct userdata *u; 47953a5a1b3Sopenharmony_ci pa_sample_spec ss; 48053a5a1b3Sopenharmony_ci pa_channel_map map; 48153a5a1b3Sopenharmony_ci pa_modargs *ma; 48253a5a1b3Sopenharmony_ci pa_sink *master=NULL; 48353a5a1b3Sopenharmony_ci pa_sink_input_new_data sink_input_data; 48453a5a1b3Sopenharmony_ci pa_sink_new_data sink_data; 48553a5a1b3Sopenharmony_ci bool use_volume_sharing = true; 48653a5a1b3Sopenharmony_ci bool force_flat_volume = false; 48753a5a1b3Sopenharmony_ci pa_memchunk silence; 48853a5a1b3Sopenharmony_ci 48953a5a1b3Sopenharmony_ci pa_assert(m); 49053a5a1b3Sopenharmony_ci 49153a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 49253a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 49353a5a1b3Sopenharmony_ci goto fail; 49453a5a1b3Sopenharmony_ci } 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) { 49753a5a1b3Sopenharmony_ci pa_log("Master sink not found"); 49853a5a1b3Sopenharmony_ci goto fail; 49953a5a1b3Sopenharmony_ci } 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci pa_assert(master); 50253a5a1b3Sopenharmony_ci 50353a5a1b3Sopenharmony_ci ss = master->sample_spec; 50453a5a1b3Sopenharmony_ci ss.format = PA_SAMPLE_FLOAT32; 50553a5a1b3Sopenharmony_ci map = master->channel_map; 50653a5a1b3Sopenharmony_ci if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { 50753a5a1b3Sopenharmony_ci pa_log("Invalid sample format specification or channel map"); 50853a5a1b3Sopenharmony_ci goto fail; 50953a5a1b3Sopenharmony_ci } 51053a5a1b3Sopenharmony_ci 51153a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) { 51253a5a1b3Sopenharmony_ci pa_log("use_volume_sharing= expects a boolean argument"); 51353a5a1b3Sopenharmony_ci goto fail; 51453a5a1b3Sopenharmony_ci } 51553a5a1b3Sopenharmony_ci 51653a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) { 51753a5a1b3Sopenharmony_ci pa_log("force_flat_volume= expects a boolean argument"); 51853a5a1b3Sopenharmony_ci goto fail; 51953a5a1b3Sopenharmony_ci } 52053a5a1b3Sopenharmony_ci 52153a5a1b3Sopenharmony_ci if (use_volume_sharing && force_flat_volume) { 52253a5a1b3Sopenharmony_ci pa_log("Flat volume can't be forced when using volume sharing."); 52353a5a1b3Sopenharmony_ci goto fail; 52453a5a1b3Sopenharmony_ci } 52553a5a1b3Sopenharmony_ci 52653a5a1b3Sopenharmony_ci u = pa_xnew0(struct userdata, 1); 52753a5a1b3Sopenharmony_ci u->module = m; 52853a5a1b3Sopenharmony_ci m->userdata = u; 52953a5a1b3Sopenharmony_ci u->channels = ss.channels; 53053a5a1b3Sopenharmony_ci 53153a5a1b3Sopenharmony_ci /* Create sink */ 53253a5a1b3Sopenharmony_ci pa_sink_new_data_init(&sink_data); 53353a5a1b3Sopenharmony_ci sink_data.driver = __FILE__; 53453a5a1b3Sopenharmony_ci sink_data.module = m; 53553a5a1b3Sopenharmony_ci if (!(sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL)))) 53653a5a1b3Sopenharmony_ci sink_data.name = pa_sprintf_malloc("%s.vsink", master->name); 53753a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&sink_data, &ss); 53853a5a1b3Sopenharmony_ci pa_sink_new_data_set_channel_map(&sink_data, &map); 53953a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); 54053a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); 54153a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, "device.vsink.name", sink_data.name); 54253a5a1b3Sopenharmony_ci 54353a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "sink_properties", sink_data.proplist, PA_UPDATE_REPLACE) < 0) { 54453a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 54553a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 54653a5a1b3Sopenharmony_ci goto fail; 54753a5a1b3Sopenharmony_ci } 54853a5a1b3Sopenharmony_ci 54953a5a1b3Sopenharmony_ci if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { 55053a5a1b3Sopenharmony_ci const char *z; 55153a5a1b3Sopenharmony_ci 55253a5a1b3Sopenharmony_ci z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); 55353a5a1b3Sopenharmony_ci pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Sink %s on %s", sink_data.name, z ? z : master->name); 55453a5a1b3Sopenharmony_ci } 55553a5a1b3Sopenharmony_ci 55653a5a1b3Sopenharmony_ci u->sink = pa_sink_new(m->core, &sink_data, (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY)) 55753a5a1b3Sopenharmony_ci | (use_volume_sharing ? PA_SINK_SHARE_VOLUME_WITH_MASTER : 0)); 55853a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 55953a5a1b3Sopenharmony_ci 56053a5a1b3Sopenharmony_ci if (!u->sink) { 56153a5a1b3Sopenharmony_ci pa_log("Failed to create sink."); 56253a5a1b3Sopenharmony_ci goto fail; 56353a5a1b3Sopenharmony_ci } 56453a5a1b3Sopenharmony_ci 56553a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg_cb; 56653a5a1b3Sopenharmony_ci u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb; 56753a5a1b3Sopenharmony_ci u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb; 56853a5a1b3Sopenharmony_ci u->sink->update_requested_latency = sink_update_requested_latency_cb; 56953a5a1b3Sopenharmony_ci u->sink->request_rewind = sink_request_rewind_cb; 57053a5a1b3Sopenharmony_ci pa_sink_set_set_mute_callback(u->sink, sink_set_mute_cb); 57153a5a1b3Sopenharmony_ci if (!use_volume_sharing) { 57253a5a1b3Sopenharmony_ci pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb); 57353a5a1b3Sopenharmony_ci pa_sink_enable_decibel_volume(u->sink, true); 57453a5a1b3Sopenharmony_ci } 57553a5a1b3Sopenharmony_ci /* Normally this flag would be enabled automatically be we can force it. */ 57653a5a1b3Sopenharmony_ci if (force_flat_volume) 57753a5a1b3Sopenharmony_ci u->sink->flags |= PA_SINK_FLAT_VOLUME; 57853a5a1b3Sopenharmony_ci u->sink->userdata = u; 57953a5a1b3Sopenharmony_ci 58053a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); 58153a5a1b3Sopenharmony_ci 58253a5a1b3Sopenharmony_ci /* Create sink input */ 58353a5a1b3Sopenharmony_ci pa_sink_input_new_data_init(&sink_input_data); 58453a5a1b3Sopenharmony_ci sink_input_data.driver = __FILE__; 58553a5a1b3Sopenharmony_ci sink_input_data.module = m; 58653a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sink(&sink_input_data, master, false, true); 58753a5a1b3Sopenharmony_ci sink_input_data.origin_sink = u->sink; 58853a5a1b3Sopenharmony_ci pa_proplist_setf(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream from %s", pa_proplist_gets(u->sink->proplist, PA_PROP_DEVICE_DESCRIPTION)); 58953a5a1b3Sopenharmony_ci pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 59053a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); 59153a5a1b3Sopenharmony_ci pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); 59253a5a1b3Sopenharmony_ci sink_input_data.flags |= PA_SINK_INPUT_START_CORKED; 59353a5a1b3Sopenharmony_ci 59453a5a1b3Sopenharmony_ci pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); 59553a5a1b3Sopenharmony_ci pa_sink_input_new_data_done(&sink_input_data); 59653a5a1b3Sopenharmony_ci 59753a5a1b3Sopenharmony_ci if (!u->sink_input) 59853a5a1b3Sopenharmony_ci goto fail; 59953a5a1b3Sopenharmony_ci 60053a5a1b3Sopenharmony_ci u->sink_input->pop = sink_input_pop_cb; 60153a5a1b3Sopenharmony_ci u->sink_input->process_rewind = sink_input_process_rewind_cb; 60253a5a1b3Sopenharmony_ci u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; 60353a5a1b3Sopenharmony_ci u->sink_input->update_max_request = sink_input_update_max_request_cb; 60453a5a1b3Sopenharmony_ci u->sink_input->update_sink_latency_range = sink_input_update_sink_latency_range_cb; 60553a5a1b3Sopenharmony_ci u->sink_input->update_sink_fixed_latency = sink_input_update_sink_fixed_latency_cb; 60653a5a1b3Sopenharmony_ci u->sink_input->kill = sink_input_kill_cb; 60753a5a1b3Sopenharmony_ci u->sink_input->attach = sink_input_attach_cb; 60853a5a1b3Sopenharmony_ci u->sink_input->detach = sink_input_detach_cb; 60953a5a1b3Sopenharmony_ci u->sink_input->moving = sink_input_moving_cb; 61053a5a1b3Sopenharmony_ci u->sink_input->volume_changed = use_volume_sharing ? NULL : sink_input_volume_changed_cb; 61153a5a1b3Sopenharmony_ci u->sink_input->mute_changed = sink_input_mute_changed_cb; 61253a5a1b3Sopenharmony_ci u->sink_input->userdata = u; 61353a5a1b3Sopenharmony_ci 61453a5a1b3Sopenharmony_ci u->sink->input_to_master = u->sink_input; 61553a5a1b3Sopenharmony_ci 61653a5a1b3Sopenharmony_ci pa_sink_input_get_silence(u->sink_input, &silence); 61753a5a1b3Sopenharmony_ci u->memblockq = pa_memblockq_new("module-virtual-sink memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, &silence); 61853a5a1b3Sopenharmony_ci pa_memblock_unref(silence.memblock); 61953a5a1b3Sopenharmony_ci 62053a5a1b3Sopenharmony_ci /* (9) INITIALIZE ANYTHING ELSE YOU NEED HERE */ 62153a5a1b3Sopenharmony_ci 62253a5a1b3Sopenharmony_ci /* The order here is important. The input must be put first, 62353a5a1b3Sopenharmony_ci * otherwise streams might attach to the sink before the sink 62453a5a1b3Sopenharmony_ci * input is attached to the master. */ 62553a5a1b3Sopenharmony_ci pa_sink_input_put(u->sink_input); 62653a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 62753a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, false); 62853a5a1b3Sopenharmony_ci 62953a5a1b3Sopenharmony_ci pa_modargs_free(ma); 63053a5a1b3Sopenharmony_ci 63153a5a1b3Sopenharmony_ci return 0; 63253a5a1b3Sopenharmony_ci 63353a5a1b3Sopenharmony_cifail: 63453a5a1b3Sopenharmony_ci if (ma) 63553a5a1b3Sopenharmony_ci pa_modargs_free(ma); 63653a5a1b3Sopenharmony_ci 63753a5a1b3Sopenharmony_ci pa__done(m); 63853a5a1b3Sopenharmony_ci 63953a5a1b3Sopenharmony_ci return -1; 64053a5a1b3Sopenharmony_ci} 64153a5a1b3Sopenharmony_ci 64253a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) { 64353a5a1b3Sopenharmony_ci struct userdata *u; 64453a5a1b3Sopenharmony_ci 64553a5a1b3Sopenharmony_ci pa_assert(m); 64653a5a1b3Sopenharmony_ci pa_assert_se(u = m->userdata); 64753a5a1b3Sopenharmony_ci 64853a5a1b3Sopenharmony_ci return pa_sink_linked_by(u->sink); 64953a5a1b3Sopenharmony_ci} 65053a5a1b3Sopenharmony_ci 65153a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 65253a5a1b3Sopenharmony_ci struct userdata *u; 65353a5a1b3Sopenharmony_ci 65453a5a1b3Sopenharmony_ci pa_assert(m); 65553a5a1b3Sopenharmony_ci 65653a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 65753a5a1b3Sopenharmony_ci return; 65853a5a1b3Sopenharmony_ci 65953a5a1b3Sopenharmony_ci /* See comments in sink_input_kill_cb() above regarding 66053a5a1b3Sopenharmony_ci * destruction order! */ 66153a5a1b3Sopenharmony_ci 66253a5a1b3Sopenharmony_ci if (u->sink_input) 66353a5a1b3Sopenharmony_ci pa_sink_input_cork(u->sink_input, true); 66453a5a1b3Sopenharmony_ci 66553a5a1b3Sopenharmony_ci if (u->sink) 66653a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 66753a5a1b3Sopenharmony_ci 66853a5a1b3Sopenharmony_ci if (u->sink_input) { 66953a5a1b3Sopenharmony_ci pa_sink_input_unlink(u->sink_input); 67053a5a1b3Sopenharmony_ci pa_sink_input_unref(u->sink_input); 67153a5a1b3Sopenharmony_ci } 67253a5a1b3Sopenharmony_ci 67353a5a1b3Sopenharmony_ci if (u->sink) 67453a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 67553a5a1b3Sopenharmony_ci 67653a5a1b3Sopenharmony_ci if (u->memblockq) 67753a5a1b3Sopenharmony_ci pa_memblockq_free(u->memblockq); 67853a5a1b3Sopenharmony_ci 67953a5a1b3Sopenharmony_ci pa_xfree(u); 68053a5a1b3Sopenharmony_ci} 681