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