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 <stdio.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 3153a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h> 3253a5a1b3Sopenharmony_ci#include <pulsecore/sink.h> 3353a5a1b3Sopenharmony_ci#include <pulsecore/module.h> 3453a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3553a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h> 3653a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 3753a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 3853a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h> 3953a5a1b3Sopenharmony_ci#include <pulsecore/ltdl-helper.h> 4053a5a1b3Sopenharmony_ci#include <pulsecore/mix.h> 4153a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h> 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Pierre-Louis Bossart"); 4453a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Virtual source"); 4553a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION); 4653a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false); 4753a5a1b3Sopenharmony_ciPA_MODULE_USAGE( 4853a5a1b3Sopenharmony_ci _("source_name=<name for the source> " 4953a5a1b3Sopenharmony_ci "source_properties=<properties for the source> " 5053a5a1b3Sopenharmony_ci "master=<name of source to filter> " 5153a5a1b3Sopenharmony_ci "uplink_sink=<name> (optional)" 5253a5a1b3Sopenharmony_ci "format=<sample format> " 5353a5a1b3Sopenharmony_ci "rate=<sample rate> " 5453a5a1b3Sopenharmony_ci "channels=<number of channels> " 5553a5a1b3Sopenharmony_ci "channel_map=<channel map> " 5653a5a1b3Sopenharmony_ci "use_volume_sharing=<yes or no> " 5753a5a1b3Sopenharmony_ci "force_flat_volume=<yes or no> " 5853a5a1b3Sopenharmony_ci )); 5953a5a1b3Sopenharmony_ci 6053a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (16*1024*1024) 6153a5a1b3Sopenharmony_ci#define BLOCK_USEC 1000 /* FIXME */ 6253a5a1b3Sopenharmony_ci 6353a5a1b3Sopenharmony_cistruct userdata { 6453a5a1b3Sopenharmony_ci pa_module *module; 6553a5a1b3Sopenharmony_ci 6653a5a1b3Sopenharmony_ci /* FIXME: Uncomment this and take "autoloaded" as a modarg if this is a filter */ 6753a5a1b3Sopenharmony_ci /* bool autoloaded; */ 6853a5a1b3Sopenharmony_ci 6953a5a1b3Sopenharmony_ci pa_source *source; 7053a5a1b3Sopenharmony_ci pa_source_output *source_output; 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci pa_memblockq *memblockq; 7353a5a1b3Sopenharmony_ci 7453a5a1b3Sopenharmony_ci bool auto_desc; 7553a5a1b3Sopenharmony_ci unsigned channels; 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_ci /* optional fields for uplink sink */ 7853a5a1b3Sopenharmony_ci pa_sink *sink; 7953a5a1b3Sopenharmony_ci pa_usec_t block_usec; 8053a5a1b3Sopenharmony_ci pa_memblockq *sink_memblockq; 8153a5a1b3Sopenharmony_ci pa_rtpoll *rtpoll; 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_ci}; 8453a5a1b3Sopenharmony_ci 8553a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = { 8653a5a1b3Sopenharmony_ci "source_name", 8753a5a1b3Sopenharmony_ci "source_properties", 8853a5a1b3Sopenharmony_ci "master", 8953a5a1b3Sopenharmony_ci "uplink_sink", 9053a5a1b3Sopenharmony_ci "format", 9153a5a1b3Sopenharmony_ci "rate", 9253a5a1b3Sopenharmony_ci "channels", 9353a5a1b3Sopenharmony_ci "channel_map", 9453a5a1b3Sopenharmony_ci "use_volume_sharing", 9553a5a1b3Sopenharmony_ci "force_flat_volume", 9653a5a1b3Sopenharmony_ci NULL 9753a5a1b3Sopenharmony_ci}; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 10053a5a1b3Sopenharmony_cistatic int sink_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_ci switch (code) { 10353a5a1b3Sopenharmony_ci 10453a5a1b3Sopenharmony_ci case PA_SINK_MESSAGE_GET_LATENCY: 10553a5a1b3Sopenharmony_ci 10653a5a1b3Sopenharmony_ci /* there's no real latency here */ 10753a5a1b3Sopenharmony_ci *((int64_t*) data) = 0; 10853a5a1b3Sopenharmony_ci 10953a5a1b3Sopenharmony_ci return 0; 11053a5a1b3Sopenharmony_ci } 11153a5a1b3Sopenharmony_ci 11253a5a1b3Sopenharmony_ci return pa_sink_process_msg(o, code, data, offset, chunk); 11353a5a1b3Sopenharmony_ci} 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci/* Called from main context */ 11653a5a1b3Sopenharmony_cistatic int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) { 11753a5a1b3Sopenharmony_ci struct userdata *u; 11853a5a1b3Sopenharmony_ci 11953a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 12053a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_ci if (!PA_SINK_IS_LINKED(state)) { 12353a5a1b3Sopenharmony_ci return 0; 12453a5a1b3Sopenharmony_ci } 12553a5a1b3Sopenharmony_ci 12653a5a1b3Sopenharmony_ci if (state == PA_SINK_RUNNING) { 12753a5a1b3Sopenharmony_ci /* need to wake-up source if it was suspended */ 12853a5a1b3Sopenharmony_ci pa_log_debug("Resuming source %s, because its uplink sink became active.", u->source->name); 12953a5a1b3Sopenharmony_ci pa_source_suspend(u->source, false, PA_SUSPEND_ALL); 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci /* FIXME: if there's no client connected, the source will suspend 13253a5a1b3Sopenharmony_ci and playback will be stuck. You'd want to prevent the source from 13353a5a1b3Sopenharmony_ci sleeping when the uplink sink is active; even if the audio is 13453a5a1b3Sopenharmony_ci discarded at least the app isn't stuck */ 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci } else { 13753a5a1b3Sopenharmony_ci /* nothing to do, if the sink becomes idle or suspended let 13853a5a1b3Sopenharmony_ci module-suspend-idle handle the sources later */ 13953a5a1b3Sopenharmony_ci } 14053a5a1b3Sopenharmony_ci 14153a5a1b3Sopenharmony_ci return 0; 14253a5a1b3Sopenharmony_ci} 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_cistatic void sink_update_requested_latency_cb(pa_sink *s) { 14553a5a1b3Sopenharmony_ci struct userdata *u; 14653a5a1b3Sopenharmony_ci 14753a5a1b3Sopenharmony_ci pa_sink_assert_ref(s); 14853a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci /* FIXME: there's no latency support */ 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_ci} 15353a5a1b3Sopenharmony_ci 15453a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 15553a5a1b3Sopenharmony_cistatic int source_process_msg_cb(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { 15653a5a1b3Sopenharmony_ci struct userdata *u = PA_SOURCE(o)->userdata; 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci switch (code) { 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci case PA_SOURCE_MESSAGE_GET_LATENCY: 16153a5a1b3Sopenharmony_ci 16253a5a1b3Sopenharmony_ci /* The source is _put() before the source output is, so let's 16353a5a1b3Sopenharmony_ci * make sure we don't access it in that time. Also, the 16453a5a1b3Sopenharmony_ci * source output is first shut down, the source second. */ 16553a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) || 16653a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) { 16753a5a1b3Sopenharmony_ci *((pa_usec_t*) data) = 0; 16853a5a1b3Sopenharmony_ci return 0; 16953a5a1b3Sopenharmony_ci } 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_ci *((pa_usec_t*) data) = 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_ci /* Get the latency of the master source */ 17453a5a1b3Sopenharmony_ci pa_source_get_latency_within_thread(u->source_output->source, true) + 17553a5a1b3Sopenharmony_ci 17653a5a1b3Sopenharmony_ci /* Add the latency internal to our source output on top */ 17753a5a1b3Sopenharmony_ci /* FIXME, no idea what I am doing here */ 17853a5a1b3Sopenharmony_ci pa_bytes_to_usec(pa_memblockq_get_length(u->source_output->thread_info.delay_memblockq), &u->source_output->source->sample_spec); 17953a5a1b3Sopenharmony_ci 18053a5a1b3Sopenharmony_ci /* Add resampler delay */ 18153a5a1b3Sopenharmony_ci *((int64_t*) data) += pa_resampler_get_delay_usec(u->source_output->thread_info.resampler); 18253a5a1b3Sopenharmony_ci 18353a5a1b3Sopenharmony_ci return 0; 18453a5a1b3Sopenharmony_ci } 18553a5a1b3Sopenharmony_ci 18653a5a1b3Sopenharmony_ci return pa_source_process_msg(o, code, data, offset, chunk); 18753a5a1b3Sopenharmony_ci} 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci/* Called from main context */ 19053a5a1b3Sopenharmony_cistatic int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause) { 19153a5a1b3Sopenharmony_ci struct userdata *u; 19253a5a1b3Sopenharmony_ci 19353a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 19453a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(state) || 19753a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 19853a5a1b3Sopenharmony_ci return 0; 19953a5a1b3Sopenharmony_ci 20053a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, state == PA_SOURCE_SUSPENDED); 20153a5a1b3Sopenharmony_ci return 0; 20253a5a1b3Sopenharmony_ci} 20353a5a1b3Sopenharmony_ci 20453a5a1b3Sopenharmony_ci/* Called from I/O thread context */ 20553a5a1b3Sopenharmony_cistatic void source_update_requested_latency_cb(pa_source *s) { 20653a5a1b3Sopenharmony_ci struct userdata *u; 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 20953a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state) || 21253a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) 21353a5a1b3Sopenharmony_ci return; 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci /* Just hand this one over to the master source */ 21653a5a1b3Sopenharmony_ci pa_source_output_set_requested_latency_within_thread( 21753a5a1b3Sopenharmony_ci u->source_output, 21853a5a1b3Sopenharmony_ci pa_source_get_requested_latency_within_thread(s)); 21953a5a1b3Sopenharmony_ci} 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci/* Called from main context */ 22253a5a1b3Sopenharmony_cistatic void source_set_volume_cb(pa_source *s) { 22353a5a1b3Sopenharmony_ci struct userdata *u; 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 22653a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(s->state) || 22953a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 23053a5a1b3Sopenharmony_ci return; 23153a5a1b3Sopenharmony_ci 23253a5a1b3Sopenharmony_ci pa_source_output_set_volume(u->source_output, &s->real_volume, s->save_volume, true); 23353a5a1b3Sopenharmony_ci} 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ci/* Called from main context */ 23653a5a1b3Sopenharmony_cistatic void source_set_mute_cb(pa_source *s) { 23753a5a1b3Sopenharmony_ci struct userdata *u; 23853a5a1b3Sopenharmony_ci 23953a5a1b3Sopenharmony_ci pa_source_assert_ref(s); 24053a5a1b3Sopenharmony_ci pa_assert_se(u = s->userdata); 24153a5a1b3Sopenharmony_ci 24253a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(s->state) || 24353a5a1b3Sopenharmony_ci !PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->state)) 24453a5a1b3Sopenharmony_ci return; 24553a5a1b3Sopenharmony_ci 24653a5a1b3Sopenharmony_ci pa_source_output_set_mute(u->source_output, s->muted, s->save_muted); 24753a5a1b3Sopenharmony_ci} 24853a5a1b3Sopenharmony_ci 24953a5a1b3Sopenharmony_ci/* Called from input thread context */ 25053a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { 25153a5a1b3Sopenharmony_ci struct userdata *u; 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 25453a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 25553a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_ci if (!PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 25853a5a1b3Sopenharmony_ci return; 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci if (!PA_SOURCE_OUTPUT_IS_LINKED(u->source_output->thread_info.state)) { 26153a5a1b3Sopenharmony_ci pa_log("push when no link?"); 26253a5a1b3Sopenharmony_ci return; 26353a5a1b3Sopenharmony_ci } 26453a5a1b3Sopenharmony_ci 26553a5a1b3Sopenharmony_ci /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */ 26653a5a1b3Sopenharmony_ci 26753a5a1b3Sopenharmony_ci /* if uplink sink exists, pull data from there; simplify by using 26853a5a1b3Sopenharmony_ci same length as chunk provided by source */ 26953a5a1b3Sopenharmony_ci if (u->sink && (u->sink->thread_info.state == PA_SINK_RUNNING)) { 27053a5a1b3Sopenharmony_ci pa_memchunk tchunk; 27153a5a1b3Sopenharmony_ci size_t nbytes = chunk->length; 27253a5a1b3Sopenharmony_ci pa_mix_info streams[2]; 27353a5a1b3Sopenharmony_ci pa_memchunk target_chunk; 27453a5a1b3Sopenharmony_ci void *target; 27553a5a1b3Sopenharmony_ci int ch; 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci /* Hmm, process any rewind request that might be queued up */ 27853a5a1b3Sopenharmony_ci pa_sink_process_rewind(u->sink, 0); 27953a5a1b3Sopenharmony_ci 28053a5a1b3Sopenharmony_ci /* get data from the sink */ 28153a5a1b3Sopenharmony_ci while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) { 28253a5a1b3Sopenharmony_ci pa_memchunk nchunk; 28353a5a1b3Sopenharmony_ci 28453a5a1b3Sopenharmony_ci /* make sure we get nbytes from the sink with render_full, 28553a5a1b3Sopenharmony_ci otherwise we cannot mix with the uplink */ 28653a5a1b3Sopenharmony_ci pa_sink_render_full(u->sink, nbytes, &nchunk); 28753a5a1b3Sopenharmony_ci pa_memblockq_push(u->sink_memblockq, &nchunk); 28853a5a1b3Sopenharmony_ci pa_memblock_unref(nchunk.memblock); 28953a5a1b3Sopenharmony_ci } 29053a5a1b3Sopenharmony_ci pa_assert(tchunk.length == chunk->length); 29153a5a1b3Sopenharmony_ci 29253a5a1b3Sopenharmony_ci /* move the read pointer for sink memblockq */ 29353a5a1b3Sopenharmony_ci pa_memblockq_drop(u->sink_memblockq, tchunk.length); 29453a5a1b3Sopenharmony_ci 29553a5a1b3Sopenharmony_ci /* allocate target chunk */ 29653a5a1b3Sopenharmony_ci /* this could probably be done in-place, but having chunk as both 29753a5a1b3Sopenharmony_ci the input and output creates issues with reference counts */ 29853a5a1b3Sopenharmony_ci target_chunk.index = 0; 29953a5a1b3Sopenharmony_ci target_chunk.length = chunk->length; 30053a5a1b3Sopenharmony_ci pa_assert(target_chunk.length == chunk->length); 30153a5a1b3Sopenharmony_ci 30253a5a1b3Sopenharmony_ci target_chunk.memblock = pa_memblock_new(o->source->core->mempool, 30353a5a1b3Sopenharmony_ci target_chunk.length); 30453a5a1b3Sopenharmony_ci pa_assert( target_chunk.memblock ); 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_ci /* get target pointer */ 30753a5a1b3Sopenharmony_ci target = pa_memblock_acquire_chunk(&target_chunk); 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_ci /* set-up mixing structure 31053a5a1b3Sopenharmony_ci volume was taken care of in sink and source already */ 31153a5a1b3Sopenharmony_ci streams[0].chunk = *chunk; 31253a5a1b3Sopenharmony_ci for(ch=0;ch<o->sample_spec.channels;ch++) 31353a5a1b3Sopenharmony_ci streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ 31453a5a1b3Sopenharmony_ci streams[0].volume.channels = o->sample_spec.channels; 31553a5a1b3Sopenharmony_ci 31653a5a1b3Sopenharmony_ci streams[1].chunk = tchunk; 31753a5a1b3Sopenharmony_ci for(ch=0;ch<o->sample_spec.channels;ch++) 31853a5a1b3Sopenharmony_ci streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ 31953a5a1b3Sopenharmony_ci streams[1].volume.channels = o->sample_spec.channels; 32053a5a1b3Sopenharmony_ci 32153a5a1b3Sopenharmony_ci /* do mixing */ 32253a5a1b3Sopenharmony_ci pa_mix(streams, /* 2 streams to be mixed */ 32353a5a1b3Sopenharmony_ci 2, 32453a5a1b3Sopenharmony_ci target, /* put result in target chunk */ 32553a5a1b3Sopenharmony_ci chunk->length, /* same length as input */ 32653a5a1b3Sopenharmony_ci (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */ 32753a5a1b3Sopenharmony_ci NULL, /* no volume information */ 32853a5a1b3Sopenharmony_ci false); /* no mute */ 32953a5a1b3Sopenharmony_ci 33053a5a1b3Sopenharmony_ci pa_memblock_release(target_chunk.memblock); 33153a5a1b3Sopenharmony_ci pa_memblock_unref(tchunk.memblock); /* clean-up */ 33253a5a1b3Sopenharmony_ci 33353a5a1b3Sopenharmony_ci /* forward the data to the virtual source */ 33453a5a1b3Sopenharmony_ci pa_source_post(u->source, &target_chunk); 33553a5a1b3Sopenharmony_ci 33653a5a1b3Sopenharmony_ci pa_memblock_unref(target_chunk.memblock); /* clean-up */ 33753a5a1b3Sopenharmony_ci 33853a5a1b3Sopenharmony_ci } else { 33953a5a1b3Sopenharmony_ci /* forward the data to the virtual source */ 34053a5a1b3Sopenharmony_ci pa_source_post(u->source, chunk); 34153a5a1b3Sopenharmony_ci } 34253a5a1b3Sopenharmony_ci 34353a5a1b3Sopenharmony_ci} 34453a5a1b3Sopenharmony_ci 34553a5a1b3Sopenharmony_ci/* Called from input thread context */ 34653a5a1b3Sopenharmony_cistatic void source_output_process_rewind_cb(pa_source_output *o, size_t nbytes) { 34753a5a1b3Sopenharmony_ci struct userdata *u; 34853a5a1b3Sopenharmony_ci 34953a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 35053a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 35153a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 35253a5a1b3Sopenharmony_ci 35353a5a1b3Sopenharmony_ci /* If the source is not yet linked, there is nothing to rewind */ 35453a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 35553a5a1b3Sopenharmony_ci pa_source_process_rewind(u->source, nbytes); 35653a5a1b3Sopenharmony_ci 35753a5a1b3Sopenharmony_ci /* FIXME, no idea what I am doing here */ 35853a5a1b3Sopenharmony_ci#if 0 35953a5a1b3Sopenharmony_ci pa_asyncmsgq_post(u->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_REWIND, NULL, (int64_t) nbytes, NULL, NULL); 36053a5a1b3Sopenharmony_ci u->send_counter -= (int64_t) nbytes; 36153a5a1b3Sopenharmony_ci#endif 36253a5a1b3Sopenharmony_ci} 36353a5a1b3Sopenharmony_ci 36453a5a1b3Sopenharmony_ci/* Called from output thread context */ 36553a5a1b3Sopenharmony_cistatic void source_output_update_max_rewind_cb(pa_source_output *o, size_t nbytes) { 36653a5a1b3Sopenharmony_ci struct userdata *u; 36753a5a1b3Sopenharmony_ci 36853a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 36953a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 37053a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 37153a5a1b3Sopenharmony_ci 37253a5a1b3Sopenharmony_ci pa_source_set_max_rewind_within_thread(u->source, nbytes); 37353a5a1b3Sopenharmony_ci} 37453a5a1b3Sopenharmony_ci 37553a5a1b3Sopenharmony_ci/* Called from output thread context */ 37653a5a1b3Sopenharmony_cistatic void source_output_attach_cb(pa_source_output *o) { 37753a5a1b3Sopenharmony_ci struct userdata *u; 37853a5a1b3Sopenharmony_ci 37953a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 38053a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 38153a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 38253a5a1b3Sopenharmony_ci 38353a5a1b3Sopenharmony_ci pa_source_set_rtpoll(u->source, o->source->thread_info.rtpoll); 38453a5a1b3Sopenharmony_ci pa_source_set_latency_range_within_thread(u->source, o->source->thread_info.min_latency, o->source->thread_info.max_latency); 38553a5a1b3Sopenharmony_ci pa_source_set_fixed_latency_within_thread(u->source, o->source->thread_info.fixed_latency); 38653a5a1b3Sopenharmony_ci pa_source_set_max_rewind_within_thread(u->source, pa_source_output_get_max_rewind(o)); 38753a5a1b3Sopenharmony_ci 38853a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 38953a5a1b3Sopenharmony_ci pa_source_attach_within_thread(u->source); 39053a5a1b3Sopenharmony_ci} 39153a5a1b3Sopenharmony_ci 39253a5a1b3Sopenharmony_ci/* Called from output thread context */ 39353a5a1b3Sopenharmony_cistatic void source_output_detach_cb(pa_source_output *o) { 39453a5a1b3Sopenharmony_ci struct userdata *u; 39553a5a1b3Sopenharmony_ci 39653a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 39753a5a1b3Sopenharmony_ci pa_source_output_assert_io_context(o); 39853a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 39953a5a1b3Sopenharmony_ci 40053a5a1b3Sopenharmony_ci if (PA_SOURCE_IS_LINKED(u->source->thread_info.state)) 40153a5a1b3Sopenharmony_ci pa_source_detach_within_thread(u->source); 40253a5a1b3Sopenharmony_ci pa_source_set_rtpoll(u->source, NULL); 40353a5a1b3Sopenharmony_ci} 40453a5a1b3Sopenharmony_ci 40553a5a1b3Sopenharmony_ci/* Called from output thread context except when cork() is called without valid source.*/ 40653a5a1b3Sopenharmony_cistatic void source_output_state_change_cb(pa_source_output *o, pa_source_output_state_t state) { 40753a5a1b3Sopenharmony_ci struct userdata *u; 40853a5a1b3Sopenharmony_ci 40953a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 41053a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 41153a5a1b3Sopenharmony_ci 41253a5a1b3Sopenharmony_ci /* FIXME */ 41353a5a1b3Sopenharmony_ci#if 0 41453a5a1b3Sopenharmony_ci if (PA_SOURCE_OUTPUT_IS_LINKED(state) && o->thread_info.state == PA_SOURCE_OUTPUT_INIT && o->source) { 41553a5a1b3Sopenharmony_ci 41653a5a1b3Sopenharmony_ci u->skip = pa_usec_to_bytes(PA_CLIP_SUB(pa_source_get_latency_within_thread(o->source, false), 41753a5a1b3Sopenharmony_ci u->latency), 41853a5a1b3Sopenharmony_ci &o->sample_spec); 41953a5a1b3Sopenharmony_ci 42053a5a1b3Sopenharmony_ci pa_log_info("Skipping %lu bytes", (unsigned long) u->skip); 42153a5a1b3Sopenharmony_ci } 42253a5a1b3Sopenharmony_ci#endif 42353a5a1b3Sopenharmony_ci} 42453a5a1b3Sopenharmony_ci 42553a5a1b3Sopenharmony_ci/* Called from main thread */ 42653a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o) { 42753a5a1b3Sopenharmony_ci struct userdata *u; 42853a5a1b3Sopenharmony_ci 42953a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 43053a5a1b3Sopenharmony_ci pa_assert_ctl_context(); 43153a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 43253a5a1b3Sopenharmony_ci 43353a5a1b3Sopenharmony_ci /* The order here matters! We first kill the source so that streams 43453a5a1b3Sopenharmony_ci * can properly be moved away while the source output is still connected 43553a5a1b3Sopenharmony_ci * to the master. */ 43653a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, true); 43753a5a1b3Sopenharmony_ci pa_source_unlink(u->source); 43853a5a1b3Sopenharmony_ci pa_source_output_unlink(u->source_output); 43953a5a1b3Sopenharmony_ci 44053a5a1b3Sopenharmony_ci pa_source_output_unref(u->source_output); 44153a5a1b3Sopenharmony_ci u->source_output = NULL; 44253a5a1b3Sopenharmony_ci 44353a5a1b3Sopenharmony_ci pa_source_unref(u->source); 44453a5a1b3Sopenharmony_ci u->source = NULL; 44553a5a1b3Sopenharmony_ci 44653a5a1b3Sopenharmony_ci pa_module_unload_request(u->module, true); 44753a5a1b3Sopenharmony_ci} 44853a5a1b3Sopenharmony_ci 44953a5a1b3Sopenharmony_ci/* Called from main thread */ 45053a5a1b3Sopenharmony_cistatic void source_output_moving_cb(pa_source_output *o, pa_source *dest) { 45153a5a1b3Sopenharmony_ci struct userdata *u; 45253a5a1b3Sopenharmony_ci uint32_t idx; 45353a5a1b3Sopenharmony_ci pa_source_output *output; 45453a5a1b3Sopenharmony_ci 45553a5a1b3Sopenharmony_ci pa_source_output_assert_ref(o); 45653a5a1b3Sopenharmony_ci pa_assert_ctl_context(); 45753a5a1b3Sopenharmony_ci pa_assert_se(u = o->userdata); 45853a5a1b3Sopenharmony_ci 45953a5a1b3Sopenharmony_ci if (dest) { 46053a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, dest->asyncmsgq); 46153a5a1b3Sopenharmony_ci pa_source_update_flags(u->source, PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY, dest->flags); 46253a5a1b3Sopenharmony_ci } else 46353a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, NULL); 46453a5a1b3Sopenharmony_ci 46553a5a1b3Sopenharmony_ci /* Propagate asyncmsq change to attached virtual sources */ 46653a5a1b3Sopenharmony_ci PA_IDXSET_FOREACH(output, u->source->outputs, idx) { 46753a5a1b3Sopenharmony_ci if (output->destination_source && output->moving) 46853a5a1b3Sopenharmony_ci output->moving(output, u->source); 46953a5a1b3Sopenharmony_ci } 47053a5a1b3Sopenharmony_ci 47153a5a1b3Sopenharmony_ci if (u->auto_desc && dest) { 47253a5a1b3Sopenharmony_ci const char *z; 47353a5a1b3Sopenharmony_ci pa_proplist *pl; 47453a5a1b3Sopenharmony_ci 47553a5a1b3Sopenharmony_ci pl = pa_proplist_new(); 47653a5a1b3Sopenharmony_ci z = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION); 47753a5a1b3Sopenharmony_ci pa_proplist_setf(pl, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", 47853a5a1b3Sopenharmony_ci pa_proplist_gets(u->source->proplist, "device.vsource.name"), z ? z : dest->name); 47953a5a1b3Sopenharmony_ci 48053a5a1b3Sopenharmony_ci pa_source_update_proplist(u->source, PA_UPDATE_REPLACE, pl); 48153a5a1b3Sopenharmony_ci pa_proplist_free(pl); 48253a5a1b3Sopenharmony_ci } 48353a5a1b3Sopenharmony_ci} 48453a5a1b3Sopenharmony_ci 48553a5a1b3Sopenharmony_ciint pa__init(pa_module*m) { 48653a5a1b3Sopenharmony_ci struct userdata *u; 48753a5a1b3Sopenharmony_ci pa_sample_spec ss; 48853a5a1b3Sopenharmony_ci pa_channel_map map; 48953a5a1b3Sopenharmony_ci pa_modargs *ma; 49053a5a1b3Sopenharmony_ci pa_source *master=NULL; 49153a5a1b3Sopenharmony_ci pa_source_output_new_data source_output_data; 49253a5a1b3Sopenharmony_ci pa_source_new_data source_data; 49353a5a1b3Sopenharmony_ci bool use_volume_sharing = true; 49453a5a1b3Sopenharmony_ci bool force_flat_volume = false; 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci /* optional for uplink_sink */ 49753a5a1b3Sopenharmony_ci pa_sink_new_data sink_data; 49853a5a1b3Sopenharmony_ci size_t nbytes; 49953a5a1b3Sopenharmony_ci 50053a5a1b3Sopenharmony_ci pa_assert(m); 50153a5a1b3Sopenharmony_ci 50253a5a1b3Sopenharmony_ci if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { 50353a5a1b3Sopenharmony_ci pa_log("Failed to parse module arguments."); 50453a5a1b3Sopenharmony_ci goto fail; 50553a5a1b3Sopenharmony_ci } 50653a5a1b3Sopenharmony_ci 50753a5a1b3Sopenharmony_ci if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SOURCE))) { 50853a5a1b3Sopenharmony_ci pa_log("Master source not found"); 50953a5a1b3Sopenharmony_ci goto fail; 51053a5a1b3Sopenharmony_ci } 51153a5a1b3Sopenharmony_ci 51253a5a1b3Sopenharmony_ci pa_assert(master); 51353a5a1b3Sopenharmony_ci 51453a5a1b3Sopenharmony_ci ss = master->sample_spec; 51553a5a1b3Sopenharmony_ci ss.format = PA_SAMPLE_FLOAT32; 51653a5a1b3Sopenharmony_ci map = master->channel_map; 51753a5a1b3Sopenharmony_ci if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { 51853a5a1b3Sopenharmony_ci pa_log("Invalid sample format specification or channel map"); 51953a5a1b3Sopenharmony_ci goto fail; 52053a5a1b3Sopenharmony_ci } 52153a5a1b3Sopenharmony_ci 52253a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "use_volume_sharing", &use_volume_sharing) < 0) { 52353a5a1b3Sopenharmony_ci pa_log("use_volume_sharing= expects a boolean argument"); 52453a5a1b3Sopenharmony_ci goto fail; 52553a5a1b3Sopenharmony_ci } 52653a5a1b3Sopenharmony_ci 52753a5a1b3Sopenharmony_ci if (pa_modargs_get_value_boolean(ma, "force_flat_volume", &force_flat_volume) < 0) { 52853a5a1b3Sopenharmony_ci pa_log("force_flat_volume= expects a boolean argument"); 52953a5a1b3Sopenharmony_ci goto fail; 53053a5a1b3Sopenharmony_ci } 53153a5a1b3Sopenharmony_ci 53253a5a1b3Sopenharmony_ci if (use_volume_sharing && force_flat_volume) { 53353a5a1b3Sopenharmony_ci pa_log("Flat volume can't be forced when using volume sharing."); 53453a5a1b3Sopenharmony_ci goto fail; 53553a5a1b3Sopenharmony_ci } 53653a5a1b3Sopenharmony_ci 53753a5a1b3Sopenharmony_ci u = pa_xnew0(struct userdata, 1); 53853a5a1b3Sopenharmony_ci u->module = m; 53953a5a1b3Sopenharmony_ci m->userdata = u; 54053a5a1b3Sopenharmony_ci u->memblockq = pa_memblockq_new("module-virtual-source memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL); 54153a5a1b3Sopenharmony_ci if (!u->memblockq) { 54253a5a1b3Sopenharmony_ci pa_log("Failed to create source memblockq."); 54353a5a1b3Sopenharmony_ci goto fail; 54453a5a1b3Sopenharmony_ci } 54553a5a1b3Sopenharmony_ci u->channels = ss.channels; 54653a5a1b3Sopenharmony_ci 54753a5a1b3Sopenharmony_ci /* The rtpoll created here is never run. It is only necessary to avoid crashes 54853a5a1b3Sopenharmony_ci * when module-virtual-source is used together with module-loopback or 54953a5a1b3Sopenharmony_ci * module-combine-sink. Both modules base their asyncmsq on the rtpoll provided 55053a5a1b3Sopenharmony_ci * by the sink. module-loopback and combine-sink only work because they 55153a5a1b3Sopenharmony_ci * call pa_asyncmsq_process_one() themselves. */ 55253a5a1b3Sopenharmony_ci u->rtpoll = pa_rtpoll_new(); 55353a5a1b3Sopenharmony_ci 55453a5a1b3Sopenharmony_ci /* Create source */ 55553a5a1b3Sopenharmony_ci pa_source_new_data_init(&source_data); 55653a5a1b3Sopenharmony_ci source_data.driver = __FILE__; 55753a5a1b3Sopenharmony_ci source_data.module = m; 55853a5a1b3Sopenharmony_ci if (!(source_data.name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL)))) 55953a5a1b3Sopenharmony_ci source_data.name = pa_sprintf_malloc("%s.vsource", master->name); 56053a5a1b3Sopenharmony_ci pa_source_new_data_set_sample_spec(&source_data, &ss); 56153a5a1b3Sopenharmony_ci pa_source_new_data_set_channel_map(&source_data, &map); 56253a5a1b3Sopenharmony_ci pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); 56353a5a1b3Sopenharmony_ci pa_proplist_sets(source_data.proplist, PA_PROP_DEVICE_CLASS, "filter"); 56453a5a1b3Sopenharmony_ci pa_proplist_sets(source_data.proplist, "device.vsource.name", source_data.name); 56553a5a1b3Sopenharmony_ci 56653a5a1b3Sopenharmony_ci if (pa_modargs_get_proplist(ma, "source_properties", source_data.proplist, PA_UPDATE_REPLACE) < 0) { 56753a5a1b3Sopenharmony_ci pa_log("Invalid properties"); 56853a5a1b3Sopenharmony_ci pa_source_new_data_done(&source_data); 56953a5a1b3Sopenharmony_ci goto fail; 57053a5a1b3Sopenharmony_ci } 57153a5a1b3Sopenharmony_ci 57253a5a1b3Sopenharmony_ci if ((u->auto_desc = !pa_proplist_contains(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { 57353a5a1b3Sopenharmony_ci const char *z; 57453a5a1b3Sopenharmony_ci 57553a5a1b3Sopenharmony_ci z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); 57653a5a1b3Sopenharmony_ci pa_proplist_setf(source_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Virtual Source %s on %s", source_data.name, z ? z : master->name); 57753a5a1b3Sopenharmony_ci } 57853a5a1b3Sopenharmony_ci 57953a5a1b3Sopenharmony_ci u->source = pa_source_new(m->core, &source_data, (master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY)) 58053a5a1b3Sopenharmony_ci | (use_volume_sharing ? PA_SOURCE_SHARE_VOLUME_WITH_MASTER : 0)); 58153a5a1b3Sopenharmony_ci 58253a5a1b3Sopenharmony_ci pa_source_new_data_done(&source_data); 58353a5a1b3Sopenharmony_ci 58453a5a1b3Sopenharmony_ci if (!u->source) { 58553a5a1b3Sopenharmony_ci pa_log("Failed to create source."); 58653a5a1b3Sopenharmony_ci goto fail; 58753a5a1b3Sopenharmony_ci } 58853a5a1b3Sopenharmony_ci 58953a5a1b3Sopenharmony_ci u->source->parent.process_msg = source_process_msg_cb; 59053a5a1b3Sopenharmony_ci u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb; 59153a5a1b3Sopenharmony_ci u->source->update_requested_latency = source_update_requested_latency_cb; 59253a5a1b3Sopenharmony_ci pa_source_set_set_mute_callback(u->source, source_set_mute_cb); 59353a5a1b3Sopenharmony_ci if (!use_volume_sharing) { 59453a5a1b3Sopenharmony_ci pa_source_set_set_volume_callback(u->source, source_set_volume_cb); 59553a5a1b3Sopenharmony_ci pa_source_enable_decibel_volume(u->source, true); 59653a5a1b3Sopenharmony_ci } 59753a5a1b3Sopenharmony_ci /* Normally this flag would be enabled automatically be we can force it. */ 59853a5a1b3Sopenharmony_ci if (force_flat_volume) 59953a5a1b3Sopenharmony_ci u->source->flags |= PA_SOURCE_FLAT_VOLUME; 60053a5a1b3Sopenharmony_ci u->source->userdata = u; 60153a5a1b3Sopenharmony_ci 60253a5a1b3Sopenharmony_ci pa_source_set_asyncmsgq(u->source, master->asyncmsgq); 60353a5a1b3Sopenharmony_ci 60453a5a1b3Sopenharmony_ci /* Create source output */ 60553a5a1b3Sopenharmony_ci pa_source_output_new_data_init(&source_output_data); 60653a5a1b3Sopenharmony_ci source_output_data.driver = __FILE__; 60753a5a1b3Sopenharmony_ci source_output_data.module = m; 60853a5a1b3Sopenharmony_ci pa_source_output_new_data_set_source(&source_output_data, master, false, true); 60953a5a1b3Sopenharmony_ci source_output_data.destination_source = u->source; 61053a5a1b3Sopenharmony_ci 61153a5a1b3Sopenharmony_ci pa_proplist_setf(source_output_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Source Stream of %s", pa_proplist_gets(u->source->proplist, PA_PROP_DEVICE_DESCRIPTION)); 61253a5a1b3Sopenharmony_ci pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); 61353a5a1b3Sopenharmony_ci pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); 61453a5a1b3Sopenharmony_ci pa_source_output_new_data_set_channel_map(&source_output_data, &map); 61553a5a1b3Sopenharmony_ci source_output_data.flags |= PA_SOURCE_OUTPUT_START_CORKED; 61653a5a1b3Sopenharmony_ci 61753a5a1b3Sopenharmony_ci pa_source_output_new(&u->source_output, m->core, &source_output_data); 61853a5a1b3Sopenharmony_ci pa_source_output_new_data_done(&source_output_data); 61953a5a1b3Sopenharmony_ci 62053a5a1b3Sopenharmony_ci if (!u->source_output) 62153a5a1b3Sopenharmony_ci goto fail; 62253a5a1b3Sopenharmony_ci 62353a5a1b3Sopenharmony_ci u->source_output->push = source_output_push_cb; 62453a5a1b3Sopenharmony_ci u->source_output->process_rewind = source_output_process_rewind_cb; 62553a5a1b3Sopenharmony_ci u->source_output->update_max_rewind = source_output_update_max_rewind_cb; 62653a5a1b3Sopenharmony_ci u->source_output->kill = source_output_kill_cb; 62753a5a1b3Sopenharmony_ci u->source_output->attach = source_output_attach_cb; 62853a5a1b3Sopenharmony_ci u->source_output->detach = source_output_detach_cb; 62953a5a1b3Sopenharmony_ci u->source_output->state_change = source_output_state_change_cb; 63053a5a1b3Sopenharmony_ci u->source_output->moving = source_output_moving_cb; 63153a5a1b3Sopenharmony_ci u->source_output->userdata = u; 63253a5a1b3Sopenharmony_ci 63353a5a1b3Sopenharmony_ci u->source->output_from_master = u->source_output; 63453a5a1b3Sopenharmony_ci 63553a5a1b3Sopenharmony_ci /* The order here is important. The output must be put first, 63653a5a1b3Sopenharmony_ci * otherwise streams might attach to the source before the 63753a5a1b3Sopenharmony_ci * source output is attached to the master. */ 63853a5a1b3Sopenharmony_ci pa_source_output_put(u->source_output); 63953a5a1b3Sopenharmony_ci pa_source_put(u->source); 64053a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, false); 64153a5a1b3Sopenharmony_ci 64253a5a1b3Sopenharmony_ci /* Create optional uplink sink */ 64353a5a1b3Sopenharmony_ci pa_sink_new_data_init(&sink_data); 64453a5a1b3Sopenharmony_ci sink_data.driver = __FILE__; 64553a5a1b3Sopenharmony_ci sink_data.module = m; 64653a5a1b3Sopenharmony_ci if ((sink_data.name = pa_xstrdup(pa_modargs_get_value(ma, "uplink_sink", NULL)))) { 64753a5a1b3Sopenharmony_ci pa_sink_new_data_set_sample_spec(&sink_data, &ss); 64853a5a1b3Sopenharmony_ci pa_sink_new_data_set_channel_map(&sink_data, &map); 64953a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_MASTER_DEVICE, master->name); 65053a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, PA_PROP_DEVICE_CLASS, "uplink sink"); 65153a5a1b3Sopenharmony_ci pa_proplist_sets(sink_data.proplist, "device.uplink_sink.name", sink_data.name); 65253a5a1b3Sopenharmony_ci 65353a5a1b3Sopenharmony_ci if ((u->auto_desc = !pa_proplist_contains(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION))) { 65453a5a1b3Sopenharmony_ci const char *z; 65553a5a1b3Sopenharmony_ci 65653a5a1b3Sopenharmony_ci z = pa_proplist_gets(master->proplist, PA_PROP_DEVICE_DESCRIPTION); 65753a5a1b3Sopenharmony_ci pa_proplist_setf(sink_data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Uplink Sink %s on %s", sink_data.name, z ? z : master->name); 65853a5a1b3Sopenharmony_ci } 65953a5a1b3Sopenharmony_ci 66053a5a1b3Sopenharmony_ci u->sink_memblockq = pa_memblockq_new("module-virtual-source sink_memblockq", 0, MEMBLOCKQ_MAXLENGTH, 0, &ss, 1, 1, 0, NULL); 66153a5a1b3Sopenharmony_ci if (!u->sink_memblockq) { 66253a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 66353a5a1b3Sopenharmony_ci pa_log("Failed to create sink memblockq."); 66453a5a1b3Sopenharmony_ci goto fail; 66553a5a1b3Sopenharmony_ci } 66653a5a1b3Sopenharmony_ci 66753a5a1b3Sopenharmony_ci u->sink = pa_sink_new(m->core, &sink_data, 0); /* FIXME, sink has no capabilities */ 66853a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 66953a5a1b3Sopenharmony_ci 67053a5a1b3Sopenharmony_ci if (!u->sink) { 67153a5a1b3Sopenharmony_ci pa_log("Failed to create sink."); 67253a5a1b3Sopenharmony_ci goto fail; 67353a5a1b3Sopenharmony_ci } 67453a5a1b3Sopenharmony_ci 67553a5a1b3Sopenharmony_ci u->sink->parent.process_msg = sink_process_msg_cb; 67653a5a1b3Sopenharmony_ci u->sink->update_requested_latency = sink_update_requested_latency_cb; 67753a5a1b3Sopenharmony_ci u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb; 67853a5a1b3Sopenharmony_ci u->sink->userdata = u; 67953a5a1b3Sopenharmony_ci 68053a5a1b3Sopenharmony_ci pa_sink_set_asyncmsgq(u->sink, master->asyncmsgq); 68153a5a1b3Sopenharmony_ci pa_sink_set_rtpoll(u->sink, u->rtpoll); 68253a5a1b3Sopenharmony_ci 68353a5a1b3Sopenharmony_ci /* FIXME: no idea what I am doing here */ 68453a5a1b3Sopenharmony_ci u->block_usec = BLOCK_USEC; 68553a5a1b3Sopenharmony_ci nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); 68653a5a1b3Sopenharmony_ci pa_sink_set_max_rewind(u->sink, 0); 68753a5a1b3Sopenharmony_ci pa_sink_set_max_request(u->sink, nbytes); 68853a5a1b3Sopenharmony_ci 68953a5a1b3Sopenharmony_ci pa_sink_put(u->sink); 69053a5a1b3Sopenharmony_ci } else { 69153a5a1b3Sopenharmony_ci pa_sink_new_data_done(&sink_data); 69253a5a1b3Sopenharmony_ci /* optional uplink sink not enabled */ 69353a5a1b3Sopenharmony_ci u->sink = NULL; 69453a5a1b3Sopenharmony_ci } 69553a5a1b3Sopenharmony_ci 69653a5a1b3Sopenharmony_ci pa_modargs_free(ma); 69753a5a1b3Sopenharmony_ci 69853a5a1b3Sopenharmony_ci return 0; 69953a5a1b3Sopenharmony_ci 70053a5a1b3Sopenharmony_cifail: 70153a5a1b3Sopenharmony_ci if (ma) 70253a5a1b3Sopenharmony_ci pa_modargs_free(ma); 70353a5a1b3Sopenharmony_ci 70453a5a1b3Sopenharmony_ci pa__done(m); 70553a5a1b3Sopenharmony_ci 70653a5a1b3Sopenharmony_ci return -1; 70753a5a1b3Sopenharmony_ci} 70853a5a1b3Sopenharmony_ci 70953a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) { 71053a5a1b3Sopenharmony_ci struct userdata *u; 71153a5a1b3Sopenharmony_ci 71253a5a1b3Sopenharmony_ci pa_assert(m); 71353a5a1b3Sopenharmony_ci pa_assert_se(u = m->userdata); 71453a5a1b3Sopenharmony_ci 71553a5a1b3Sopenharmony_ci return pa_source_linked_by(u->source); 71653a5a1b3Sopenharmony_ci} 71753a5a1b3Sopenharmony_ci 71853a5a1b3Sopenharmony_civoid pa__done(pa_module*m) { 71953a5a1b3Sopenharmony_ci struct userdata *u; 72053a5a1b3Sopenharmony_ci 72153a5a1b3Sopenharmony_ci pa_assert(m); 72253a5a1b3Sopenharmony_ci 72353a5a1b3Sopenharmony_ci if (!(u = m->userdata)) 72453a5a1b3Sopenharmony_ci return; 72553a5a1b3Sopenharmony_ci 72653a5a1b3Sopenharmony_ci /* See comments in source_output_kill_cb() above regarding 72753a5a1b3Sopenharmony_ci * destruction order! */ 72853a5a1b3Sopenharmony_ci 72953a5a1b3Sopenharmony_ci if (u->source_output) 73053a5a1b3Sopenharmony_ci pa_source_output_cork(u->source_output, true); 73153a5a1b3Sopenharmony_ci 73253a5a1b3Sopenharmony_ci if (u->source) 73353a5a1b3Sopenharmony_ci pa_source_unlink(u->source); 73453a5a1b3Sopenharmony_ci 73553a5a1b3Sopenharmony_ci if (u->source_output) { 73653a5a1b3Sopenharmony_ci pa_source_output_unlink(u->source_output); 73753a5a1b3Sopenharmony_ci pa_source_output_unref(u->source_output); 73853a5a1b3Sopenharmony_ci } 73953a5a1b3Sopenharmony_ci 74053a5a1b3Sopenharmony_ci if (u->source) 74153a5a1b3Sopenharmony_ci pa_source_unref(u->source); 74253a5a1b3Sopenharmony_ci 74353a5a1b3Sopenharmony_ci if (u->sink) { 74453a5a1b3Sopenharmony_ci pa_sink_unlink(u->sink); 74553a5a1b3Sopenharmony_ci pa_sink_unref(u->sink); 74653a5a1b3Sopenharmony_ci } 74753a5a1b3Sopenharmony_ci 74853a5a1b3Sopenharmony_ci if (u->memblockq) 74953a5a1b3Sopenharmony_ci pa_memblockq_free(u->memblockq); 75053a5a1b3Sopenharmony_ci 75153a5a1b3Sopenharmony_ci if (u->sink_memblockq) 75253a5a1b3Sopenharmony_ci pa_memblockq_free(u->sink_memblockq); 75353a5a1b3Sopenharmony_ci 75453a5a1b3Sopenharmony_ci if (u->rtpoll) 75553a5a1b3Sopenharmony_ci pa_rtpoll_free(u->rtpoll); 75653a5a1b3Sopenharmony_ci 75753a5a1b3Sopenharmony_ci pa_xfree(u); 75853a5a1b3Sopenharmony_ci} 759