153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2008 Lennart Poettering
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stdlib.h>
2553a5a1b3Sopenharmony_ci#include <stdio.h>
2653a5a1b3Sopenharmony_ci#include <errno.h>
2753a5a1b3Sopenharmony_ci#include <unistd.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3053a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3153a5a1b3Sopenharmony_ci#include <pulse/util.h>
3253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/sink.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/rtpoll.h>
4453a5a1b3Sopenharmony_ci
4553a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
4653a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION(_("Clocked NULL sink"));
4753a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
4853a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
4953a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
5053a5a1b3Sopenharmony_ci        "sink_name=<name of sink> "
5153a5a1b3Sopenharmony_ci        "sink_properties=<properties for the sink> "
5253a5a1b3Sopenharmony_ci        "format=<sample format> "
5353a5a1b3Sopenharmony_ci        "rate=<sample rate> "
5453a5a1b3Sopenharmony_ci        "channels=<number of channels> "
5553a5a1b3Sopenharmony_ci        "channel_map=<channel map>"
5653a5a1b3Sopenharmony_ci        "formats=<semi-colon separated sink formats>"
5753a5a1b3Sopenharmony_ci        "norewinds=<disable rewinds>");
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ci#define DEFAULT_SINK_NAME "null"
6053a5a1b3Sopenharmony_ci#define BLOCK_USEC (2 * PA_USEC_PER_SEC)
6153a5a1b3Sopenharmony_ci#define BLOCK_USEC_NOREWINDS (50 * PA_USEC_PER_MSEC)
6253a5a1b3Sopenharmony_ci
6353a5a1b3Sopenharmony_cistruct userdata {
6453a5a1b3Sopenharmony_ci    pa_core *core;
6553a5a1b3Sopenharmony_ci    pa_module *module;
6653a5a1b3Sopenharmony_ci    pa_sink *sink;
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci    pa_thread *thread;
6953a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
7053a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_ci    pa_usec_t block_usec;
7353a5a1b3Sopenharmony_ci    pa_usec_t timestamp;
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci    pa_idxset *formats;
7653a5a1b3Sopenharmony_ci
7753a5a1b3Sopenharmony_ci    bool norewinds;
7853a5a1b3Sopenharmony_ci};
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
8153a5a1b3Sopenharmony_ci    "sink_name",
8253a5a1b3Sopenharmony_ci    "sink_properties",
8353a5a1b3Sopenharmony_ci    "format",
8453a5a1b3Sopenharmony_ci    "rate",
8553a5a1b3Sopenharmony_ci    "channels",
8653a5a1b3Sopenharmony_ci    "channel_map",
8753a5a1b3Sopenharmony_ci    "formats",
8853a5a1b3Sopenharmony_ci    "norewinds",
8953a5a1b3Sopenharmony_ci    NULL
9053a5a1b3Sopenharmony_ci};
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_cistatic int sink_process_msg(
9353a5a1b3Sopenharmony_ci        pa_msgobject *o,
9453a5a1b3Sopenharmony_ci        int code,
9553a5a1b3Sopenharmony_ci        void *data,
9653a5a1b3Sopenharmony_ci        int64_t offset,
9753a5a1b3Sopenharmony_ci        pa_memchunk *chunk) {
9853a5a1b3Sopenharmony_ci
9953a5a1b3Sopenharmony_ci    struct userdata *u = PA_SINK(o)->userdata;
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_ci    switch (code) {
10253a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY: {
10353a5a1b3Sopenharmony_ci            pa_usec_t now;
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci            now = pa_rtclock_now();
10653a5a1b3Sopenharmony_ci            *((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now;
10753a5a1b3Sopenharmony_ci
10853a5a1b3Sopenharmony_ci            return 0;
10953a5a1b3Sopenharmony_ci        }
11053a5a1b3Sopenharmony_ci    }
11153a5a1b3Sopenharmony_ci
11253a5a1b3Sopenharmony_ci    return pa_sink_process_msg(o, code, data, offset, chunk);
11353a5a1b3Sopenharmony_ci}
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci/* Called from the IO thread. */
11653a5a1b3Sopenharmony_cistatic void sink_recalculate_max_request_and_rewind(pa_sink *s) {
11753a5a1b3Sopenharmony_ci    struct userdata *u;
11853a5a1b3Sopenharmony_ci    size_t nbytes;
11953a5a1b3Sopenharmony_ci
12053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
12153a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci    nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec);
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    if (u->norewinds) {
12653a5a1b3Sopenharmony_ci        pa_sink_set_max_rewind_within_thread(s, 0);
12753a5a1b3Sopenharmony_ci    } else {
12853a5a1b3Sopenharmony_ci        pa_sink_set_max_rewind_within_thread(s, nbytes);
12953a5a1b3Sopenharmony_ci    }
13053a5a1b3Sopenharmony_ci
13153a5a1b3Sopenharmony_ci    pa_sink_set_max_request_within_thread(s, nbytes);
13253a5a1b3Sopenharmony_ci}
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci/* Called from the IO thread. */
13553a5a1b3Sopenharmony_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) {
13653a5a1b3Sopenharmony_ci    struct userdata *u;
13753a5a1b3Sopenharmony_ci
13853a5a1b3Sopenharmony_ci    pa_assert(s);
13953a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_ci    if (s->thread_info.state == PA_SINK_SUSPENDED || s->thread_info.state == PA_SINK_INIT) {
14253a5a1b3Sopenharmony_ci        if (PA_SINK_IS_OPENED(new_state)) {
14353a5a1b3Sopenharmony_ci            u->timestamp = pa_rtclock_now();
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci            /* If sink was suspended to change sample formats, both
14653a5a1b3Sopenharmony_ci             * thread_info.max_request and thread_info.max_rewind
14753a5a1b3Sopenharmony_ci             * must be updated before first block is rendered
14853a5a1b3Sopenharmony_ci             */
14953a5a1b3Sopenharmony_ci            sink_recalculate_max_request_and_rewind(s);
15053a5a1b3Sopenharmony_ci        }
15153a5a1b3Sopenharmony_ci    }
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    return 0;
15453a5a1b3Sopenharmony_ci}
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_ci/* Called from the IO thread. */
15753a5a1b3Sopenharmony_cistatic void sink_update_requested_latency_cb(pa_sink *s) {
15853a5a1b3Sopenharmony_ci    struct userdata *u;
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
16153a5a1b3Sopenharmony_ci    pa_assert_se(u = s->userdata);
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci    u->block_usec = pa_sink_get_requested_latency_within_thread(s);
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci    if (u->block_usec == (pa_usec_t) -1)
16653a5a1b3Sopenharmony_ci        u->block_usec = s->thread_info.max_latency;
16753a5a1b3Sopenharmony_ci
16853a5a1b3Sopenharmony_ci    sink_recalculate_max_request_and_rewind(s);
16953a5a1b3Sopenharmony_ci}
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_cistatic void sink_reconfigure_cb(pa_sink *s, pa_sample_spec *spec, bool passthrough) {
17253a5a1b3Sopenharmony_ci    /* We don't need to do anything */
17353a5a1b3Sopenharmony_ci    s->sample_spec = *spec;
17453a5a1b3Sopenharmony_ci}
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_cistatic bool sink_set_formats_cb(pa_sink *s, pa_idxset *formats) {
17753a5a1b3Sopenharmony_ci    struct userdata *u = s->userdata;
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci    pa_assert(u);
18053a5a1b3Sopenharmony_ci
18153a5a1b3Sopenharmony_ci    pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free);
18253a5a1b3Sopenharmony_ci    u->formats = pa_idxset_copy(formats, (pa_copy_func_t) pa_format_info_copy);
18353a5a1b3Sopenharmony_ci
18453a5a1b3Sopenharmony_ci    return true;
18553a5a1b3Sopenharmony_ci}
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_cistatic pa_idxset* sink_get_formats_cb(pa_sink *s) {
18853a5a1b3Sopenharmony_ci    struct userdata *u = s->userdata;
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_ci    pa_assert(u);
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    return pa_idxset_copy(u->formats, (pa_copy_func_t) pa_format_info_copy);
19353a5a1b3Sopenharmony_ci}
19453a5a1b3Sopenharmony_ci
19553a5a1b3Sopenharmony_cistatic void process_rewind(struct userdata *u, pa_usec_t now) {
19653a5a1b3Sopenharmony_ci    size_t rewind_nbytes, in_buffer;
19753a5a1b3Sopenharmony_ci    pa_usec_t delay;
19853a5a1b3Sopenharmony_ci
19953a5a1b3Sopenharmony_ci    pa_assert(u);
20053a5a1b3Sopenharmony_ci
20153a5a1b3Sopenharmony_ci    rewind_nbytes = u->sink->thread_info.rewind_nbytes;
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0)
20453a5a1b3Sopenharmony_ci        goto do_nothing;
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci    pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci    if (u->timestamp <= now)
20953a5a1b3Sopenharmony_ci        goto do_nothing;
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci    delay = u->timestamp - now;
21253a5a1b3Sopenharmony_ci    in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec);
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci    if (in_buffer <= 0)
21553a5a1b3Sopenharmony_ci        goto do_nothing;
21653a5a1b3Sopenharmony_ci
21753a5a1b3Sopenharmony_ci    if (rewind_nbytes > in_buffer)
21853a5a1b3Sopenharmony_ci        rewind_nbytes = in_buffer;
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci    pa_sink_process_rewind(u->sink, rewind_nbytes);
22153a5a1b3Sopenharmony_ci    u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci    pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
22453a5a1b3Sopenharmony_ci    return;
22553a5a1b3Sopenharmony_ci
22653a5a1b3Sopenharmony_cido_nothing:
22753a5a1b3Sopenharmony_ci
22853a5a1b3Sopenharmony_ci    pa_sink_process_rewind(u->sink, 0);
22953a5a1b3Sopenharmony_ci}
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_cistatic void process_render(struct userdata *u, pa_usec_t now) {
23253a5a1b3Sopenharmony_ci    size_t ate = 0;
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    pa_assert(u);
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci    /* This is the configured latency. Sink inputs connected to us
23753a5a1b3Sopenharmony_ci    might not have a single frame more than the maxrequest value
23853a5a1b3Sopenharmony_ci    queued. Hence: at maximum read this many bytes from the sink
23953a5a1b3Sopenharmony_ci    inputs. */
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    /* Fill the buffer up the latency size */
24253a5a1b3Sopenharmony_ci    while (u->timestamp < now + u->block_usec) {
24353a5a1b3Sopenharmony_ci        pa_memchunk chunk;
24453a5a1b3Sopenharmony_ci        size_t request_size;
24553a5a1b3Sopenharmony_ci
24653a5a1b3Sopenharmony_ci        request_size = pa_usec_to_bytes(now + u->block_usec - u->timestamp, &u->sink->sample_spec);
24753a5a1b3Sopenharmony_ci        request_size = PA_MIN(request_size, u->sink->thread_info.max_request);
24853a5a1b3Sopenharmony_ci        pa_sink_render(u->sink, request_size, &chunk);
24953a5a1b3Sopenharmony_ci
25053a5a1b3Sopenharmony_ci        pa_memblock_unref(chunk.memblock);
25153a5a1b3Sopenharmony_ci
25253a5a1b3Sopenharmony_ci/*         pa_log_debug("Ate %lu bytes.", (unsigned long) chunk.length); */
25353a5a1b3Sopenharmony_ci        u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
25453a5a1b3Sopenharmony_ci
25553a5a1b3Sopenharmony_ci        ate += chunk.length;
25653a5a1b3Sopenharmony_ci
25753a5a1b3Sopenharmony_ci        if (ate >= u->sink->thread_info.max_request)
25853a5a1b3Sopenharmony_ci            break;
25953a5a1b3Sopenharmony_ci    }
26053a5a1b3Sopenharmony_ci
26153a5a1b3Sopenharmony_ci/*     pa_log_debug("Ate in sum %lu bytes (of %lu)", (unsigned long) ate, (unsigned long) nbytes); */
26253a5a1b3Sopenharmony_ci}
26353a5a1b3Sopenharmony_ci
26453a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
26553a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
26653a5a1b3Sopenharmony_ci
26753a5a1b3Sopenharmony_ci    pa_assert(u);
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
27053a5a1b3Sopenharmony_ci
27153a5a1b3Sopenharmony_ci    if (u->core->realtime_scheduling)
27253a5a1b3Sopenharmony_ci        pa_thread_make_realtime(u->core->realtime_priority);
27353a5a1b3Sopenharmony_ci
27453a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
27553a5a1b3Sopenharmony_ci
27653a5a1b3Sopenharmony_ci    u->timestamp = pa_rtclock_now();
27753a5a1b3Sopenharmony_ci
27853a5a1b3Sopenharmony_ci    for (;;) {
27953a5a1b3Sopenharmony_ci        pa_usec_t now = 0;
28053a5a1b3Sopenharmony_ci        int ret;
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci        if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
28353a5a1b3Sopenharmony_ci            now = pa_rtclock_now();
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
28653a5a1b3Sopenharmony_ci            process_rewind(u, now);
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci        /* Render some data and drop it immediately */
28953a5a1b3Sopenharmony_ci        if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
29053a5a1b3Sopenharmony_ci            if (u->timestamp <= now)
29153a5a1b3Sopenharmony_ci                process_render(u, now);
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
29453a5a1b3Sopenharmony_ci        } else
29553a5a1b3Sopenharmony_ci            pa_rtpoll_set_timer_disabled(u->rtpoll);
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_ci        /* Hmm, nothing to do. Let's sleep */
29853a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
29953a5a1b3Sopenharmony_ci            goto fail;
30053a5a1b3Sopenharmony_ci
30153a5a1b3Sopenharmony_ci        if (ret == 0)
30253a5a1b3Sopenharmony_ci            goto finish;
30353a5a1b3Sopenharmony_ci    }
30453a5a1b3Sopenharmony_ci
30553a5a1b3Sopenharmony_cifail:
30653a5a1b3Sopenharmony_ci    /* If this was no regular exit from the loop we have to continue
30753a5a1b3Sopenharmony_ci     * processing messages until we received PA_MESSAGE_SHUTDOWN */
30853a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
30953a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
31053a5a1b3Sopenharmony_ci
31153a5a1b3Sopenharmony_cifinish:
31253a5a1b3Sopenharmony_ci    pa_log_debug("Thread shutting down");
31353a5a1b3Sopenharmony_ci}
31453a5a1b3Sopenharmony_ci
31553a5a1b3Sopenharmony_ciint pa__init(pa_module*m) {
31653a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
31753a5a1b3Sopenharmony_ci    pa_sample_spec ss;
31853a5a1b3Sopenharmony_ci    pa_channel_map map;
31953a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
32053a5a1b3Sopenharmony_ci    pa_sink_new_data data;
32153a5a1b3Sopenharmony_ci    pa_format_info *format;
32253a5a1b3Sopenharmony_ci    const char *formats;
32353a5a1b3Sopenharmony_ci    size_t nbytes;
32453a5a1b3Sopenharmony_ci
32553a5a1b3Sopenharmony_ci    pa_assert(m);
32653a5a1b3Sopenharmony_ci
32753a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
32853a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments.");
32953a5a1b3Sopenharmony_ci        goto fail;
33053a5a1b3Sopenharmony_ci    }
33153a5a1b3Sopenharmony_ci
33253a5a1b3Sopenharmony_ci    ss = m->core->default_sample_spec;
33353a5a1b3Sopenharmony_ci    map = m->core->default_channel_map;
33453a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
33553a5a1b3Sopenharmony_ci        pa_log("Invalid sample format specification or channel map");
33653a5a1b3Sopenharmony_ci        goto fail;
33753a5a1b3Sopenharmony_ci    }
33853a5a1b3Sopenharmony_ci
33953a5a1b3Sopenharmony_ci    m->userdata = u = pa_xnew0(struct userdata, 1);
34053a5a1b3Sopenharmony_ci    u->core = m->core;
34153a5a1b3Sopenharmony_ci    u->module = m;
34253a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
34353a5a1b3Sopenharmony_ci    u->block_usec = BLOCK_USEC;
34453a5a1b3Sopenharmony_ci
34553a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
34653a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
34753a5a1b3Sopenharmony_ci        goto fail;
34853a5a1b3Sopenharmony_ci    }
34953a5a1b3Sopenharmony_ci
35053a5a1b3Sopenharmony_ci    pa_sink_new_data_init(&data);
35153a5a1b3Sopenharmony_ci    data.driver = __FILE__;
35253a5a1b3Sopenharmony_ci    data.module = m;
35353a5a1b3Sopenharmony_ci    pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
35453a5a1b3Sopenharmony_ci    pa_sink_new_data_set_sample_spec(&data, &ss);
35553a5a1b3Sopenharmony_ci    pa_sink_new_data_set_channel_map(&data, &map);
35653a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, _("Null Output"));
35753a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    u->formats = pa_idxset_new(NULL, NULL);
36053a5a1b3Sopenharmony_ci    if ((formats = pa_modargs_get_value(ma, "formats", NULL))) {
36153a5a1b3Sopenharmony_ci        char *f = NULL;
36253a5a1b3Sopenharmony_ci        const char *state = NULL;
36353a5a1b3Sopenharmony_ci
36453a5a1b3Sopenharmony_ci        while ((f = pa_split(formats, ";", &state))) {
36553a5a1b3Sopenharmony_ci            format = pa_format_info_from_string(pa_strip(f));
36653a5a1b3Sopenharmony_ci
36753a5a1b3Sopenharmony_ci            if (!format) {
36853a5a1b3Sopenharmony_ci                pa_log(_("Failed to set format: invalid format string %s"), f);
36953a5a1b3Sopenharmony_ci		pa_xfree(f);
37053a5a1b3Sopenharmony_ci                goto fail;
37153a5a1b3Sopenharmony_ci            }
37253a5a1b3Sopenharmony_ci            pa_xfree(f);
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_ci            pa_idxset_put(u->formats, format, NULL);
37553a5a1b3Sopenharmony_ci        }
37653a5a1b3Sopenharmony_ci    } else {
37753a5a1b3Sopenharmony_ci        format = pa_format_info_new();
37853a5a1b3Sopenharmony_ci        format->encoding = PA_ENCODING_PCM;
37953a5a1b3Sopenharmony_ci        pa_idxset_put(u->formats, format, NULL);
38053a5a1b3Sopenharmony_ci    }
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ci    if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
38353a5a1b3Sopenharmony_ci        pa_log("Invalid properties");
38453a5a1b3Sopenharmony_ci        pa_sink_new_data_done(&data);
38553a5a1b3Sopenharmony_ci        goto fail;
38653a5a1b3Sopenharmony_ci    }
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci    u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | PA_SINK_SET_FORMATS);
38953a5a1b3Sopenharmony_ci    pa_sink_new_data_done(&data);
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci    if (!u->sink) {
39253a5a1b3Sopenharmony_ci        pa_log("Failed to create sink object.");
39353a5a1b3Sopenharmony_ci        goto fail;
39453a5a1b3Sopenharmony_ci    }
39553a5a1b3Sopenharmony_ci
39653a5a1b3Sopenharmony_ci    u->sink->parent.process_msg = sink_process_msg;
39753a5a1b3Sopenharmony_ci    u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
39853a5a1b3Sopenharmony_ci    u->sink->update_requested_latency = sink_update_requested_latency_cb;
39953a5a1b3Sopenharmony_ci    u->sink->reconfigure = sink_reconfigure_cb;
40053a5a1b3Sopenharmony_ci    u->sink->get_formats = sink_get_formats_cb;
40153a5a1b3Sopenharmony_ci    u->sink->set_formats = sink_set_formats_cb;
40253a5a1b3Sopenharmony_ci    u->sink->userdata = u;
40353a5a1b3Sopenharmony_ci
40453a5a1b3Sopenharmony_ci    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
40553a5a1b3Sopenharmony_ci    pa_sink_set_rtpoll(u->sink, u->rtpoll);
40653a5a1b3Sopenharmony_ci
40753a5a1b3Sopenharmony_ci    if(pa_modargs_get_value_boolean(ma, "norewinds", &u->norewinds) < 0){
40853a5a1b3Sopenharmony_ci        pa_log("Invalid argument, norewinds expects a boolean value.");
40953a5a1b3Sopenharmony_ci    }
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_ci    if (u->norewinds)
41253a5a1b3Sopenharmony_ci        u->block_usec = BLOCK_USEC_NOREWINDS;
41353a5a1b3Sopenharmony_ci
41453a5a1b3Sopenharmony_ci    nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec);
41553a5a1b3Sopenharmony_ci
41653a5a1b3Sopenharmony_ci    if(u->norewinds){
41753a5a1b3Sopenharmony_ci        pa_sink_set_max_rewind(u->sink, 0);
41853a5a1b3Sopenharmony_ci    } else {
41953a5a1b3Sopenharmony_ci        pa_sink_set_max_rewind(u->sink, nbytes);
42053a5a1b3Sopenharmony_ci    }
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_ci    pa_sink_set_max_request(u->sink, nbytes);
42353a5a1b3Sopenharmony_ci
42453a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("null-sink", thread_func, u))) {
42553a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
42653a5a1b3Sopenharmony_ci        goto fail;
42753a5a1b3Sopenharmony_ci    }
42853a5a1b3Sopenharmony_ci
42953a5a1b3Sopenharmony_ci    pa_sink_set_latency_range(u->sink, 0, u->block_usec);
43053a5a1b3Sopenharmony_ci
43153a5a1b3Sopenharmony_ci    pa_sink_put(u->sink);
43253a5a1b3Sopenharmony_ci
43353a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
43453a5a1b3Sopenharmony_ci
43553a5a1b3Sopenharmony_ci    return 0;
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_cifail:
43853a5a1b3Sopenharmony_ci    if (ma)
43953a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
44053a5a1b3Sopenharmony_ci
44153a5a1b3Sopenharmony_ci    pa__done(m);
44253a5a1b3Sopenharmony_ci
44353a5a1b3Sopenharmony_ci    return -1;
44453a5a1b3Sopenharmony_ci}
44553a5a1b3Sopenharmony_ci
44653a5a1b3Sopenharmony_ciint pa__get_n_used(pa_module *m) {
44753a5a1b3Sopenharmony_ci    struct userdata *u;
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    pa_assert(m);
45053a5a1b3Sopenharmony_ci    pa_assert_se(u = m->userdata);
45153a5a1b3Sopenharmony_ci
45253a5a1b3Sopenharmony_ci    return pa_sink_linked_by(u->sink);
45353a5a1b3Sopenharmony_ci}
45453a5a1b3Sopenharmony_ci
45553a5a1b3Sopenharmony_civoid pa__done(pa_module*m) {
45653a5a1b3Sopenharmony_ci    struct userdata *u;
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci    pa_assert(m);
45953a5a1b3Sopenharmony_ci
46053a5a1b3Sopenharmony_ci    if (!(u = m->userdata))
46153a5a1b3Sopenharmony_ci        return;
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci    if (u->sink)
46453a5a1b3Sopenharmony_ci        pa_sink_unlink(u->sink);
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci    if (u->thread) {
46753a5a1b3Sopenharmony_ci        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
46853a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
46953a5a1b3Sopenharmony_ci    }
47053a5a1b3Sopenharmony_ci
47153a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
47253a5a1b3Sopenharmony_ci
47353a5a1b3Sopenharmony_ci    if (u->sink)
47453a5a1b3Sopenharmony_ci        pa_sink_unref(u->sink);
47553a5a1b3Sopenharmony_ci
47653a5a1b3Sopenharmony_ci    if (u->rtpoll)
47753a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
47853a5a1b3Sopenharmony_ci
47953a5a1b3Sopenharmony_ci    if (u->formats)
48053a5a1b3Sopenharmony_ci        pa_idxset_free(u->formats, (pa_free_cb_t) pa_format_info_free);
48153a5a1b3Sopenharmony_ci
48253a5a1b3Sopenharmony_ci    pa_xfree(u);
48353a5a1b3Sopenharmony_ci}
484