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