153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2004-2006 Lennart Poettering
553a5a1b3Sopenharmony_ci  Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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#ifndef LOG_TAG
2653a5a1b3Sopenharmony_ci#define LOG_TAG "ProtocolNative"
2753a5a1b3Sopenharmony_ci#endif
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <string.h>
3053a5a1b3Sopenharmony_ci#include <stdio.h>
3153a5a1b3Sopenharmony_ci#include <stdlib.h>
3253a5a1b3Sopenharmony_ci#include <unistd.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3553a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3653a5a1b3Sopenharmony_ci#include <pulse/version.h>
3753a5a1b3Sopenharmony_ci#include <pulse/utf8.h>
3853a5a1b3Sopenharmony_ci#include <pulse/util.h>
3953a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
4053a5a1b3Sopenharmony_ci#include <pulse/internal.h>
4153a5a1b3Sopenharmony_ci
4253a5a1b3Sopenharmony_ci#include <pulsecore/native-common.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/packet.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/client.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/source-output.h>
4653a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h>
4753a5a1b3Sopenharmony_ci#include <pulsecore/pstream.h>
4853a5a1b3Sopenharmony_ci#include <pulsecore/tagstruct.h>
4953a5a1b3Sopenharmony_ci#include <pulsecore/pdispatch.h>
5053a5a1b3Sopenharmony_ci#include <pulsecore/pstream-util.h>
5153a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
5253a5a1b3Sopenharmony_ci#include <pulsecore/core-scache.h>
5353a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h>
5453a5a1b3Sopenharmony_ci#include <pulsecore/message-handler.h>
5553a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
5653a5a1b3Sopenharmony_ci#include <pulsecore/mem.h>
5753a5a1b3Sopenharmony_ci#include <pulsecore/strlist.h>
5853a5a1b3Sopenharmony_ci#include <pulsecore/shared.h>
5953a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
6053a5a1b3Sopenharmony_ci#include <pulsecore/creds.h>
6153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
6253a5a1b3Sopenharmony_ci#include <pulsecore/ipacl.h>
6353a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
6453a5a1b3Sopenharmony_ci#include <pulsecore/mem.h>
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_ci#include "protocol-native.h"
6753a5a1b3Sopenharmony_ci
6853a5a1b3Sopenharmony_ci/* #define PROTOCOL_NATIVE_DEBUG */
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci/* Kick a client if it doesn't authenticate within this time */
7153a5a1b3Sopenharmony_ci#define AUTH_TIMEOUT (60 * PA_USEC_PER_SEC)
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci/* Don't accept more connection than this */
7453a5a1b3Sopenharmony_ci#define MAX_CONNECTIONS 128
7553a5a1b3Sopenharmony_ci
7653a5a1b3Sopenharmony_ci#define MAX_MEMBLOCKQ_LENGTH (4*1024*1024) /* 4MB */
7753a5a1b3Sopenharmony_ci#define DEFAULT_TLENGTH_MSEC 2000 /* 2s */
7853a5a1b3Sopenharmony_ci#define DEFAULT_PROCESS_MSEC 20   /* 20ms */
7953a5a1b3Sopenharmony_ci#define DEFAULT_FRAGSIZE_MSEC DEFAULT_TLENGTH_MSEC
8053a5a1b3Sopenharmony_ci
8153a5a1b3Sopenharmony_cistatic bool sink_input_process_underrun_cb(pa_sink_input *i);
8253a5a1b3Sopenharmony_cistatic bool sink_input_process_underrun_ohos_cb(pa_sink_input *i);
8353a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
8453a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i);
8553a5a1b3Sopenharmony_cistatic void sink_input_suspend_cb(pa_sink_input *i, pa_sink_state_t old_state, pa_suspend_cause_t old_suspend_cause);
8653a5a1b3Sopenharmony_cistatic void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest);
8753a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
8853a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
8953a5a1b3Sopenharmony_cistatic void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes);
9053a5a1b3Sopenharmony_cistatic void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl);
9153a5a1b3Sopenharmony_ci
9253a5a1b3Sopenharmony_cistatic void native_connection_send_memblock(pa_native_connection *c);
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o);
9553a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
9653a5a1b3Sopenharmony_cistatic void source_output_suspend_cb(pa_source_output *o, pa_source_state_t old_state, pa_suspend_cause_t old_suspend_cause);
9753a5a1b3Sopenharmony_cistatic void source_output_moving_cb(pa_source_output *o, pa_source *dest);
9853a5a1b3Sopenharmony_cistatic pa_usec_t source_output_get_latency_cb(pa_source_output *o);
9953a5a1b3Sopenharmony_cistatic void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl);
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_cistatic int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
10253a5a1b3Sopenharmony_cistatic int source_output_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
10353a5a1b3Sopenharmony_ci
10453a5a1b3Sopenharmony_ci/* structure management */
10553a5a1b3Sopenharmony_ci
10653a5a1b3Sopenharmony_ci/* Called from main context */
10753a5a1b3Sopenharmony_cistatic void upload_stream_unlink(upload_stream *s) {
10853a5a1b3Sopenharmony_ci    pa_assert(s);
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ci    if (!s->connection)
11153a5a1b3Sopenharmony_ci        return;
11253a5a1b3Sopenharmony_ci
11353a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
11453a5a1b3Sopenharmony_ci    s->connection = NULL;
11553a5a1b3Sopenharmony_ci    upload_stream_unref(s);
11653a5a1b3Sopenharmony_ci}
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci/* Called from main context */
11953a5a1b3Sopenharmony_cistatic void upload_stream_free(pa_object *o) {
12053a5a1b3Sopenharmony_ci    upload_stream *s = UPLOAD_STREAM(o);
12153a5a1b3Sopenharmony_ci    pa_assert(s);
12253a5a1b3Sopenharmony_ci
12353a5a1b3Sopenharmony_ci    upload_stream_unlink(s);
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    pa_xfree(s->name);
12653a5a1b3Sopenharmony_ci
12753a5a1b3Sopenharmony_ci    if (s->proplist)
12853a5a1b3Sopenharmony_ci        pa_proplist_free(s->proplist);
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    if (s->memchunk.memblock)
13153a5a1b3Sopenharmony_ci        pa_memblock_unref(s->memchunk.memblock);
13253a5a1b3Sopenharmony_ci
13353a5a1b3Sopenharmony_ci    pa_xfree(s);
13453a5a1b3Sopenharmony_ci}
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci/* Called from main context */
13753a5a1b3Sopenharmony_cistatic upload_stream* upload_stream_new(
13853a5a1b3Sopenharmony_ci        pa_native_connection *c,
13953a5a1b3Sopenharmony_ci        const pa_sample_spec *ss,
14053a5a1b3Sopenharmony_ci        const pa_channel_map *map,
14153a5a1b3Sopenharmony_ci        const char *name,
14253a5a1b3Sopenharmony_ci        size_t length,
14353a5a1b3Sopenharmony_ci        pa_proplist *p) {
14453a5a1b3Sopenharmony_ci
14553a5a1b3Sopenharmony_ci    upload_stream *s;
14653a5a1b3Sopenharmony_ci
14753a5a1b3Sopenharmony_ci    pa_assert(c);
14853a5a1b3Sopenharmony_ci    pa_assert(ss);
14953a5a1b3Sopenharmony_ci    pa_assert(name);
15053a5a1b3Sopenharmony_ci    pa_assert(length > 0);
15153a5a1b3Sopenharmony_ci    pa_assert(p);
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_ci    s = pa_msgobject_new(upload_stream);
15453a5a1b3Sopenharmony_ci    s->parent.parent.parent.free = upload_stream_free;
15553a5a1b3Sopenharmony_ci    s->connection = c;
15653a5a1b3Sopenharmony_ci    s->sample_spec = *ss;
15753a5a1b3Sopenharmony_ci    s->channel_map = *map;
15853a5a1b3Sopenharmony_ci    s->name = pa_xstrdup(name);
15953a5a1b3Sopenharmony_ci    pa_memchunk_reset(&s->memchunk);
16053a5a1b3Sopenharmony_ci    s->length = length;
16153a5a1b3Sopenharmony_ci    s->proplist = pa_proplist_copy(p);
16253a5a1b3Sopenharmony_ci    pa_proplist_update(s->proplist, PA_UPDATE_MERGE, c->client->proplist);
16353a5a1b3Sopenharmony_ci
16453a5a1b3Sopenharmony_ci    pa_idxset_put(c->output_streams, s, &s->index);
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    return s;
16753a5a1b3Sopenharmony_ci}
16853a5a1b3Sopenharmony_ci
16953a5a1b3Sopenharmony_ci/* Called from main context */
17053a5a1b3Sopenharmony_cistatic void record_stream_unlink(record_stream *s) {
17153a5a1b3Sopenharmony_ci    pa_assert(s);
17253a5a1b3Sopenharmony_ci
17353a5a1b3Sopenharmony_ci    if (!s->connection)
17453a5a1b3Sopenharmony_ci        return;
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci    if (s->source_output) {
17753a5a1b3Sopenharmony_ci        pa_source_output_unlink(s->source_output);
17853a5a1b3Sopenharmony_ci        pa_source_output_unref(s->source_output);
17953a5a1b3Sopenharmony_ci        s->source_output = NULL;
18053a5a1b3Sopenharmony_ci    }
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_remove_by_data(s->connection->record_streams, s, NULL) == s);
18353a5a1b3Sopenharmony_ci    s->connection = NULL;
18453a5a1b3Sopenharmony_ci    record_stream_unref(s);
18553a5a1b3Sopenharmony_ci}
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci/* Called from main context */
18853a5a1b3Sopenharmony_cistatic void record_stream_free(pa_object *o) {
18953a5a1b3Sopenharmony_ci    record_stream *s = RECORD_STREAM(o);
19053a5a1b3Sopenharmony_ci    pa_assert(s);
19153a5a1b3Sopenharmony_ci
19253a5a1b3Sopenharmony_ci    record_stream_unlink(s);
19353a5a1b3Sopenharmony_ci
19453a5a1b3Sopenharmony_ci    pa_memblockq_free(s->memblockq);
19553a5a1b3Sopenharmony_ci    pa_xfree(s);
19653a5a1b3Sopenharmony_ci}
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci/* Called from main context */
19953a5a1b3Sopenharmony_cistatic int record_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
20053a5a1b3Sopenharmony_ci    record_stream *s = RECORD_STREAM(o);
20153a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
20253a5a1b3Sopenharmony_ci
20353a5a1b3Sopenharmony_ci    if (!s->connection)
20453a5a1b3Sopenharmony_ci        return -1;
20553a5a1b3Sopenharmony_ci
20653a5a1b3Sopenharmony_ci    switch (code) {
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci        case RECORD_STREAM_MESSAGE_POST_DATA:
20953a5a1b3Sopenharmony_ci
21053a5a1b3Sopenharmony_ci            /* We try to keep up to date with how many bytes are
21153a5a1b3Sopenharmony_ci             * currently on the fly */
21253a5a1b3Sopenharmony_ci            pa_atomic_sub(&s->on_the_fly, chunk->length);
21353a5a1b3Sopenharmony_ci
21453a5a1b3Sopenharmony_ci            if (pa_memblockq_push_align(s->memblockq, chunk) < 0) {
21553a5a1b3Sopenharmony_ci/*                 pa_log_warn("Failed to push data into output queue."); */
21653a5a1b3Sopenharmony_ci                return -1;
21753a5a1b3Sopenharmony_ci            }
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci            if (!pa_pstream_is_pending(s->connection->pstream))
22053a5a1b3Sopenharmony_ci                native_connection_send_memblock(s->connection);
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci            break;
22353a5a1b3Sopenharmony_ci    }
22453a5a1b3Sopenharmony_ci
22553a5a1b3Sopenharmony_ci    return 0;
22653a5a1b3Sopenharmony_ci}
22753a5a1b3Sopenharmony_ci
22853a5a1b3Sopenharmony_ci/* Called from main context */
22953a5a1b3Sopenharmony_cistatic void fix_record_buffer_attr_pre(record_stream *s) {
23053a5a1b3Sopenharmony_ci
23153a5a1b3Sopenharmony_ci    size_t frame_size;
23253a5a1b3Sopenharmony_ci    pa_usec_t orig_fragsize_usec, fragsize_usec, source_usec;
23353a5a1b3Sopenharmony_ci
23453a5a1b3Sopenharmony_ci    pa_assert(s);
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci    /* This function will be called from the main thread, before as
23753a5a1b3Sopenharmony_ci     * well as after the source output has been activated using
23853a5a1b3Sopenharmony_ci     * pa_source_output_put()! That means it may not touch any
23953a5a1b3Sopenharmony_ci     * ->thread_info data! */
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    frame_size = pa_frame_size(&s->source_output->sample_spec);
24253a5a1b3Sopenharmony_ci    s->buffer_attr = s->buffer_attr_req;
24353a5a1b3Sopenharmony_ci
24453a5a1b3Sopenharmony_ci    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
24553a5a1b3Sopenharmony_ci        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
24653a5a1b3Sopenharmony_ci    if (s->buffer_attr.maxlength <= 0)
24753a5a1b3Sopenharmony_ci        s->buffer_attr.maxlength = (uint32_t) frame_size;
24853a5a1b3Sopenharmony_ci
24953a5a1b3Sopenharmony_ci    if (s->buffer_attr.fragsize == (uint32_t) -1)
25053a5a1b3Sopenharmony_ci        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(DEFAULT_FRAGSIZE_MSEC*PA_USEC_PER_MSEC, &s->source_output->sample_spec);
25153a5a1b3Sopenharmony_ci    if (s->buffer_attr.fragsize <= 0)
25253a5a1b3Sopenharmony_ci        s->buffer_attr.fragsize = (uint32_t) frame_size;
25353a5a1b3Sopenharmony_ci
25453a5a1b3Sopenharmony_ci    orig_fragsize_usec = fragsize_usec = pa_bytes_to_usec(s->buffer_attr.fragsize, &s->source_output->sample_spec);
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci    if (s->early_requests) {
25753a5a1b3Sopenharmony_ci
25853a5a1b3Sopenharmony_ci        /* In early request mode we need to emulate the classic
25953a5a1b3Sopenharmony_ci         * fragment-based playback model. Unfortunately we have no
26053a5a1b3Sopenharmony_ci         * mechanism to tell the source how often we want it to send us
26153a5a1b3Sopenharmony_ci         * data. The next best thing we can do is to set the source's
26253a5a1b3Sopenharmony_ci         * total buffer (i.e. its latency) to the fragment size. That
26353a5a1b3Sopenharmony_ci         * way it will have to send data at least that often. */
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci        source_usec = fragsize_usec;
26653a5a1b3Sopenharmony_ci
26753a5a1b3Sopenharmony_ci    } else if (s->adjust_latency) {
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci        /* So, the user asked us to adjust the latency according to
27053a5a1b3Sopenharmony_ci         * what the source can provide. We set the source to whatever
27153a5a1b3Sopenharmony_ci         * latency it can provide that is closest to what we want, and
27253a5a1b3Sopenharmony_ci         * let the client buffer be equally large. This does NOT mean
27353a5a1b3Sopenharmony_ci         * that we are doing (2 * fragsize) bytes of buffering, since
27453a5a1b3Sopenharmony_ci         * the client-side buffer is only data that is on the way to
27553a5a1b3Sopenharmony_ci         * the client. */
27653a5a1b3Sopenharmony_ci
27753a5a1b3Sopenharmony_ci        source_usec = fragsize_usec;
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_ci    } else {
28053a5a1b3Sopenharmony_ci
28153a5a1b3Sopenharmony_ci        /* Ok, the user didn't ask us to adjust the latency, hence we
28253a5a1b3Sopenharmony_ci         * don't */
28353a5a1b3Sopenharmony_ci
28453a5a1b3Sopenharmony_ci        source_usec = (pa_usec_t) -1;
28553a5a1b3Sopenharmony_ci    }
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_ci    if (source_usec != (pa_usec_t) -1)
28853a5a1b3Sopenharmony_ci        s->configured_source_latency = pa_source_output_set_requested_latency(s->source_output, source_usec);
28953a5a1b3Sopenharmony_ci    else
29053a5a1b3Sopenharmony_ci        s->configured_source_latency = 0;
29153a5a1b3Sopenharmony_ci
29253a5a1b3Sopenharmony_ci    if (s->early_requests) {
29353a5a1b3Sopenharmony_ci
29453a5a1b3Sopenharmony_ci        /* Ok, we didn't necessarily get what we were asking for. We
29553a5a1b3Sopenharmony_ci         * might still get the proper fragment interval, we just can't
29653a5a1b3Sopenharmony_ci         * guarantee it. */
29753a5a1b3Sopenharmony_ci
29853a5a1b3Sopenharmony_ci        if (fragsize_usec != s->configured_source_latency)
29953a5a1b3Sopenharmony_ci            pa_log_debug("Could not configure a sufficiently low latency. Early requests might not be satisfied.");
30053a5a1b3Sopenharmony_ci
30153a5a1b3Sopenharmony_ci    } else if (s->adjust_latency) {
30253a5a1b3Sopenharmony_ci
30353a5a1b3Sopenharmony_ci        /* We keep the client buffer large enough to transfer one
30453a5a1b3Sopenharmony_ci         * hardware-buffer-sized chunk at a time to the client. */
30553a5a1b3Sopenharmony_ci
30653a5a1b3Sopenharmony_ci        fragsize_usec = s->configured_source_latency;
30753a5a1b3Sopenharmony_ci    }
30853a5a1b3Sopenharmony_ci
30953a5a1b3Sopenharmony_ci    if (pa_usec_to_bytes(orig_fragsize_usec, &s->source_output->sample_spec) !=
31053a5a1b3Sopenharmony_ci        pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec))
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci        s->buffer_attr.fragsize = (uint32_t) pa_usec_to_bytes(fragsize_usec, &s->source_output->sample_spec);
31353a5a1b3Sopenharmony_ci
31453a5a1b3Sopenharmony_ci    if (s->buffer_attr.fragsize <= 0)
31553a5a1b3Sopenharmony_ci        s->buffer_attr.fragsize = (uint32_t) frame_size;
31653a5a1b3Sopenharmony_ci}
31753a5a1b3Sopenharmony_ci
31853a5a1b3Sopenharmony_ci/* Called from main context */
31953a5a1b3Sopenharmony_cistatic void fix_record_buffer_attr_post(record_stream *s) {
32053a5a1b3Sopenharmony_ci    size_t base;
32153a5a1b3Sopenharmony_ci
32253a5a1b3Sopenharmony_ci    pa_assert(s);
32353a5a1b3Sopenharmony_ci
32453a5a1b3Sopenharmony_ci    /* This function will be called from the main thread, before as
32553a5a1b3Sopenharmony_ci     * well as after the source output has been activated using
32653a5a1b3Sopenharmony_ci     * pa_source_output_put()! That means it may not touch and
32753a5a1b3Sopenharmony_ci     * ->thread_info data! */
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    base = pa_frame_size(&s->source_output->sample_spec);
33053a5a1b3Sopenharmony_ci
33153a5a1b3Sopenharmony_ci    s->buffer_attr.fragsize = (s->buffer_attr.fragsize/base)*base;
33253a5a1b3Sopenharmony_ci    if (s->buffer_attr.fragsize <= 0)
33353a5a1b3Sopenharmony_ci        s->buffer_attr.fragsize = base;
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_ci    if (s->buffer_attr.fragsize > s->buffer_attr.maxlength)
33653a5a1b3Sopenharmony_ci        s->buffer_attr.fragsize = s->buffer_attr.maxlength;
33753a5a1b3Sopenharmony_ci}
33853a5a1b3Sopenharmony_ci
33953a5a1b3Sopenharmony_ci/* Called from main context */
34053a5a1b3Sopenharmony_cistatic record_stream* record_stream_new(
34153a5a1b3Sopenharmony_ci        pa_native_connection *c,
34253a5a1b3Sopenharmony_ci        pa_source *source,
34353a5a1b3Sopenharmony_ci        pa_sample_spec *ss,
34453a5a1b3Sopenharmony_ci        pa_channel_map *map,
34553a5a1b3Sopenharmony_ci        pa_idxset *formats,
34653a5a1b3Sopenharmony_ci        pa_buffer_attr *attr,
34753a5a1b3Sopenharmony_ci        pa_cvolume *volume,
34853a5a1b3Sopenharmony_ci        bool muted,
34953a5a1b3Sopenharmony_ci        bool muted_set,
35053a5a1b3Sopenharmony_ci        pa_source_output_flags_t flags,
35153a5a1b3Sopenharmony_ci        pa_proplist *p,
35253a5a1b3Sopenharmony_ci        bool adjust_latency,
35353a5a1b3Sopenharmony_ci        bool early_requests,
35453a5a1b3Sopenharmony_ci        bool relative_volume,
35553a5a1b3Sopenharmony_ci        bool peak_detect,
35653a5a1b3Sopenharmony_ci        pa_sink_input *direct_on_input,
35753a5a1b3Sopenharmony_ci        int *ret) {
35853a5a1b3Sopenharmony_ci
35953a5a1b3Sopenharmony_ci    /* Note: This function takes ownership of the 'formats' param, so we need
36053a5a1b3Sopenharmony_ci     * to take extra care to not leak it */
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_ci    record_stream *s;
36353a5a1b3Sopenharmony_ci    pa_source_output *source_output = NULL;
36453a5a1b3Sopenharmony_ci    pa_source_output_new_data data;
36553a5a1b3Sopenharmony_ci    char *memblockq_name;
36653a5a1b3Sopenharmony_ci
36753a5a1b3Sopenharmony_ci    pa_assert(c);
36853a5a1b3Sopenharmony_ci    pa_assert(ss);
36953a5a1b3Sopenharmony_ci    pa_assert(p);
37053a5a1b3Sopenharmony_ci    pa_assert(ret);
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    pa_source_output_new_data_init(&data);
37353a5a1b3Sopenharmony_ci
37453a5a1b3Sopenharmony_ci    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
37553a5a1b3Sopenharmony_ci    data.driver = __FILE__;
37653a5a1b3Sopenharmony_ci    data.module = c->options->module;
37753a5a1b3Sopenharmony_ci    data.client = c->client;
37853a5a1b3Sopenharmony_ci    if (source)
37953a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_source(&data, source, false, true);
38053a5a1b3Sopenharmony_ci    if (pa_sample_spec_valid(ss))
38153a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_sample_spec(&data, ss);
38253a5a1b3Sopenharmony_ci    if (pa_channel_map_valid(map))
38353a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_channel_map(&data, map);
38453a5a1b3Sopenharmony_ci    if (formats)
38553a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_formats(&data, formats);
38653a5a1b3Sopenharmony_ci    data.direct_on_input = direct_on_input;
38753a5a1b3Sopenharmony_ci    if (volume) {
38853a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_volume(&data, volume);
38953a5a1b3Sopenharmony_ci        data.volume_is_absolute = !relative_volume;
39053a5a1b3Sopenharmony_ci        data.save_volume = false;
39153a5a1b3Sopenharmony_ci    }
39253a5a1b3Sopenharmony_ci    if (muted_set) {
39353a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_muted(&data, muted);
39453a5a1b3Sopenharmony_ci        data.save_muted = false;
39553a5a1b3Sopenharmony_ci    }
39653a5a1b3Sopenharmony_ci    if (peak_detect)
39753a5a1b3Sopenharmony_ci        data.resample_method = PA_RESAMPLER_PEAKS;
39853a5a1b3Sopenharmony_ci    data.flags = flags;
39953a5a1b3Sopenharmony_ci
40053a5a1b3Sopenharmony_ci    *ret = -pa_source_output_new(&source_output, c->protocol->core, &data);
40153a5a1b3Sopenharmony_ci
40253a5a1b3Sopenharmony_ci    pa_source_output_new_data_done(&data);
40353a5a1b3Sopenharmony_ci
40453a5a1b3Sopenharmony_ci    if (!source_output)
40553a5a1b3Sopenharmony_ci        return NULL;
40653a5a1b3Sopenharmony_ci
40753a5a1b3Sopenharmony_ci    s = pa_msgobject_new(record_stream);
40853a5a1b3Sopenharmony_ci    s->parent.parent.free = record_stream_free;
40953a5a1b3Sopenharmony_ci    s->parent.process_msg = record_stream_process_msg;
41053a5a1b3Sopenharmony_ci    s->connection = c;
41153a5a1b3Sopenharmony_ci    s->source_output = source_output;
41253a5a1b3Sopenharmony_ci    s->buffer_attr_req = *attr;
41353a5a1b3Sopenharmony_ci    s->adjust_latency = adjust_latency;
41453a5a1b3Sopenharmony_ci    s->early_requests = early_requests;
41553a5a1b3Sopenharmony_ci    pa_atomic_store(&s->on_the_fly, 0);
41653a5a1b3Sopenharmony_ci
41753a5a1b3Sopenharmony_ci    s->source_output->parent.process_msg = source_output_process_msg;
41853a5a1b3Sopenharmony_ci    s->source_output->push = source_output_push_cb;
41953a5a1b3Sopenharmony_ci    s->source_output->kill = source_output_kill_cb;
42053a5a1b3Sopenharmony_ci    s->source_output->get_latency = source_output_get_latency_cb;
42153a5a1b3Sopenharmony_ci    s->source_output->moving = source_output_moving_cb;
42253a5a1b3Sopenharmony_ci    s->source_output->suspend = source_output_suspend_cb;
42353a5a1b3Sopenharmony_ci    s->source_output->send_event = source_output_send_event_cb;
42453a5a1b3Sopenharmony_ci    s->source_output->userdata = s;
42553a5a1b3Sopenharmony_ci
42653a5a1b3Sopenharmony_ci    fix_record_buffer_attr_pre(s);
42753a5a1b3Sopenharmony_ci
42853a5a1b3Sopenharmony_ci    memblockq_name = pa_sprintf_malloc("native protocol record stream memblockq [%u]", s->source_output->index);
42953a5a1b3Sopenharmony_ci    s->memblockq = pa_memblockq_new(
43053a5a1b3Sopenharmony_ci            memblockq_name,
43153a5a1b3Sopenharmony_ci            0,
43253a5a1b3Sopenharmony_ci            s->buffer_attr.maxlength,
43353a5a1b3Sopenharmony_ci            0,
43453a5a1b3Sopenharmony_ci            &source_output->sample_spec,
43553a5a1b3Sopenharmony_ci            1,
43653a5a1b3Sopenharmony_ci            0,
43753a5a1b3Sopenharmony_ci            0,
43853a5a1b3Sopenharmony_ci            NULL);
43953a5a1b3Sopenharmony_ci    pa_xfree(memblockq_name);
44053a5a1b3Sopenharmony_ci
44153a5a1b3Sopenharmony_ci    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
44253a5a1b3Sopenharmony_ci    fix_record_buffer_attr_post(s);
44353a5a1b3Sopenharmony_ci
44453a5a1b3Sopenharmony_ci    *ss = s->source_output->sample_spec;
44553a5a1b3Sopenharmony_ci    *map = s->source_output->channel_map;
44653a5a1b3Sopenharmony_ci
44753a5a1b3Sopenharmony_ci    pa_idxset_put(c->record_streams, s, &s->index);
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    pa_log_debug("Final latency %0.2f ms = %0.2f ms + %0.2f ms",
45053a5a1b3Sopenharmony_ci                ((double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) + (double) s->configured_source_latency) / PA_USEC_PER_MSEC,
45153a5a1b3Sopenharmony_ci                (double) pa_bytes_to_usec(s->buffer_attr.fragsize, &source_output->sample_spec) / PA_USEC_PER_MSEC,
45253a5a1b3Sopenharmony_ci                (double) s->configured_source_latency / PA_USEC_PER_MSEC);
45353a5a1b3Sopenharmony_ci
45453a5a1b3Sopenharmony_ci    pa_source_output_put(s->source_output);
45553a5a1b3Sopenharmony_ci    return s;
45653a5a1b3Sopenharmony_ci}
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci/* Called from main context */
45953a5a1b3Sopenharmony_cistatic void record_stream_send_killed(record_stream *r) {
46053a5a1b3Sopenharmony_ci    pa_tagstruct *t;
46153a5a1b3Sopenharmony_ci    record_stream_assert_ref(r);
46253a5a1b3Sopenharmony_ci
46353a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
46453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_KILLED);
46553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
46653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, r->index);
46753a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(r->connection->pstream, t);
46853a5a1b3Sopenharmony_ci}
46953a5a1b3Sopenharmony_ci
47053a5a1b3Sopenharmony_ci/* Called from main context */
47153a5a1b3Sopenharmony_cistatic void playback_stream_unlink(playback_stream *s) {
47253a5a1b3Sopenharmony_ci    pa_assert(s);
47353a5a1b3Sopenharmony_ci
47453a5a1b3Sopenharmony_ci    if (!s->connection)
47553a5a1b3Sopenharmony_ci        return;
47653a5a1b3Sopenharmony_ci
47753a5a1b3Sopenharmony_ci    if (s->sink_input) {
47853a5a1b3Sopenharmony_ci        pa_sink_input_unlink(s->sink_input);
47953a5a1b3Sopenharmony_ci        pa_sink_input_unref(s->sink_input);
48053a5a1b3Sopenharmony_ci        s->sink_input = NULL;
48153a5a1b3Sopenharmony_ci    }
48253a5a1b3Sopenharmony_ci
48353a5a1b3Sopenharmony_ci    if (s->drain_request)
48453a5a1b3Sopenharmony_ci        pa_pstream_send_error(s->connection->pstream, s->drain_tag, PA_ERR_NOENTITY);
48553a5a1b3Sopenharmony_ci
48653a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_remove_by_data(s->connection->output_streams, s, NULL) == s);
48753a5a1b3Sopenharmony_ci    s->connection = NULL;
48853a5a1b3Sopenharmony_ci    playback_stream_unref(s);
48953a5a1b3Sopenharmony_ci}
49053a5a1b3Sopenharmony_ci
49153a5a1b3Sopenharmony_ci/* Called from main context */
49253a5a1b3Sopenharmony_cistatic void playback_stream_free(pa_object* o) {
49353a5a1b3Sopenharmony_ci    playback_stream *s = PLAYBACK_STREAM(o);
49453a5a1b3Sopenharmony_ci    pa_assert(s);
49553a5a1b3Sopenharmony_ci
49653a5a1b3Sopenharmony_ci    playback_stream_unlink(s);
49753a5a1b3Sopenharmony_ci
49853a5a1b3Sopenharmony_ci    pa_memblockq_free(s->memblockq);
49953a5a1b3Sopenharmony_ci    pa_xfree(s);
50053a5a1b3Sopenharmony_ci}
50153a5a1b3Sopenharmony_ci
50253a5a1b3Sopenharmony_ci/* Called from main context */
50353a5a1b3Sopenharmony_cistatic int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
50453a5a1b3Sopenharmony_ci    playback_stream *s = PLAYBACK_STREAM(o);
50553a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
50653a5a1b3Sopenharmony_ci
50753a5a1b3Sopenharmony_ci    if (!s->connection)
50853a5a1b3Sopenharmony_ci        return -1;
50953a5a1b3Sopenharmony_ci
51053a5a1b3Sopenharmony_ci    switch (code) {
51153a5a1b3Sopenharmony_ci
51253a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_REQUEST_DATA: {
51353a5a1b3Sopenharmony_ci            pa_tagstruct *t;
51453a5a1b3Sopenharmony_ci            int l = 0;
51553a5a1b3Sopenharmony_ci
51653a5a1b3Sopenharmony_ci            for (;;) {
51753a5a1b3Sopenharmony_ci                if ((l = pa_atomic_load(&s->missing)) <= 0)
51853a5a1b3Sopenharmony_ci                    return 0;
51953a5a1b3Sopenharmony_ci
52053a5a1b3Sopenharmony_ci                if (pa_atomic_cmpxchg(&s->missing, l, 0))
52153a5a1b3Sopenharmony_ci                    break;
52253a5a1b3Sopenharmony_ci            }
52353a5a1b3Sopenharmony_ci
52453a5a1b3Sopenharmony_ci            t = pa_tagstruct_new();
52553a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, PA_COMMAND_REQUEST);
52653a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
52753a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, s->index);
52853a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, (uint32_t) l);
52953a5a1b3Sopenharmony_ci            pa_pstream_send_tagstruct(s->connection->pstream, t);
53053a5a1b3Sopenharmony_ci
53153a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
53253a5a1b3Sopenharmony_ci            pa_log("Requesting %lu bytes", (unsigned long) l);
53353a5a1b3Sopenharmony_ci#endif
53453a5a1b3Sopenharmony_ci            break;
53553a5a1b3Sopenharmony_ci        }
53653a5a1b3Sopenharmony_ci
53753a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_UNDERFLOW: {
53853a5a1b3Sopenharmony_ci            pa_tagstruct *t;
53953a5a1b3Sopenharmony_ci
54053a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
54153a5a1b3Sopenharmony_ci            pa_log("signalling underflow");
54253a5a1b3Sopenharmony_ci#endif
54353a5a1b3Sopenharmony_ci
54453a5a1b3Sopenharmony_ci            /* Report that we're empty */
54553a5a1b3Sopenharmony_ci            t = pa_tagstruct_new();
54653a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW);
54753a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
54853a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, s->index);
54953a5a1b3Sopenharmony_ci            if (s->connection->version >= 23)
55053a5a1b3Sopenharmony_ci                pa_tagstruct_puts64(t, offset);
55153a5a1b3Sopenharmony_ci            pa_pstream_send_tagstruct(s->connection->pstream, t);
55253a5a1b3Sopenharmony_ci            break;
55353a5a1b3Sopenharmony_ci        }
55453a5a1b3Sopenharmony_ci
55553a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_OVERFLOW: {
55653a5a1b3Sopenharmony_ci            pa_tagstruct *t;
55753a5a1b3Sopenharmony_ci
55853a5a1b3Sopenharmony_ci            /* Notify the user we're overflowed*/
55953a5a1b3Sopenharmony_ci            t = pa_tagstruct_new();
56053a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, PA_COMMAND_OVERFLOW);
56153a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
56253a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, s->index);
56353a5a1b3Sopenharmony_ci            pa_pstream_send_tagstruct(s->connection->pstream, t);
56453a5a1b3Sopenharmony_ci            break;
56553a5a1b3Sopenharmony_ci        }
56653a5a1b3Sopenharmony_ci
56753a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_STARTED:
56853a5a1b3Sopenharmony_ci
56953a5a1b3Sopenharmony_ci            if (s->connection->version >= 13) {
57053a5a1b3Sopenharmony_ci                pa_tagstruct *t;
57153a5a1b3Sopenharmony_ci
57253a5a1b3Sopenharmony_ci                /* Notify the user we started playback */
57353a5a1b3Sopenharmony_ci                t = pa_tagstruct_new();
57453a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, PA_COMMAND_STARTED);
57553a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
57653a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, s->index);
57753a5a1b3Sopenharmony_ci                pa_pstream_send_tagstruct(s->connection->pstream, t);
57853a5a1b3Sopenharmony_ci            }
57953a5a1b3Sopenharmony_ci
58053a5a1b3Sopenharmony_ci            break;
58153a5a1b3Sopenharmony_ci
58253a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_DRAIN_ACK:
58353a5a1b3Sopenharmony_ci            pa_pstream_send_simple_ack(s->connection->pstream, PA_PTR_TO_UINT(userdata));
58453a5a1b3Sopenharmony_ci            break;
58553a5a1b3Sopenharmony_ci
58653a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH:
58753a5a1b3Sopenharmony_ci
58853a5a1b3Sopenharmony_ci            s->buffer_attr.tlength = (uint32_t) offset;
58953a5a1b3Sopenharmony_ci
59053a5a1b3Sopenharmony_ci            if (s->connection->version >= 15) {
59153a5a1b3Sopenharmony_ci                pa_tagstruct *t;
59253a5a1b3Sopenharmony_ci
59353a5a1b3Sopenharmony_ci                t = pa_tagstruct_new();
59453a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED);
59553a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
59653a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, s->index);
59753a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
59853a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, s->buffer_attr.tlength);
59953a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
60053a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, s->buffer_attr.minreq);
60153a5a1b3Sopenharmony_ci                pa_tagstruct_put_usec(t, s->configured_sink_latency);
60253a5a1b3Sopenharmony_ci                pa_pstream_send_tagstruct(s->connection->pstream, t);
60353a5a1b3Sopenharmony_ci            }
60453a5a1b3Sopenharmony_ci
60553a5a1b3Sopenharmony_ci        case PLAYBACK_STREAM_MESSAGE_UNDERFLOW_OHOS: {
60653a5a1b3Sopenharmony_ci            pa_tagstruct *t;
60753a5a1b3Sopenharmony_ci
60853a5a1b3Sopenharmony_ci            /* Notify the user we're overflowed*/
60953a5a1b3Sopenharmony_ci            t = pa_tagstruct_new();
61053a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, PA_COMMAND_UNDERFLOW_OHOS);
61153a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
61253a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, s->index);
61353a5a1b3Sopenharmony_ci            pa_pstream_send_tagstruct(s->connection->pstream, t);
61453a5a1b3Sopenharmony_ci            break;
61553a5a1b3Sopenharmony_ci        }
61653a5a1b3Sopenharmony_ci
61753a5a1b3Sopenharmony_ci            break;
61853a5a1b3Sopenharmony_ci    }
61953a5a1b3Sopenharmony_ci
62053a5a1b3Sopenharmony_ci    return 0;
62153a5a1b3Sopenharmony_ci}
62253a5a1b3Sopenharmony_ci
62353a5a1b3Sopenharmony_ci/* Called from main context */
62453a5a1b3Sopenharmony_cistatic void fix_playback_buffer_attr(playback_stream *s) {
62553a5a1b3Sopenharmony_ci    size_t frame_size, max_prebuf;
62653a5a1b3Sopenharmony_ci    pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
62753a5a1b3Sopenharmony_ci
62853a5a1b3Sopenharmony_ci    pa_assert(s);
62953a5a1b3Sopenharmony_ci
63053a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
63153a5a1b3Sopenharmony_ci    pa_log_debug("Client requested: maxlength=%li bytes tlength=%li bytes minreq=%li bytes prebuf=%li bytes",
63253a5a1b3Sopenharmony_ci           (long) s->buffer_attr_req.maxlength,
63353a5a1b3Sopenharmony_ci           (long) s->buffer_attr_req.tlength,
63453a5a1b3Sopenharmony_ci           (long) s->buffer_attr_req.minreq,
63553a5a1b3Sopenharmony_ci           (long) s->buffer_attr_req.prebuf);
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci    pa_log("Client requested: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms",
63853a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
63953a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
64053a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
64153a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr_req.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC));
64253a5a1b3Sopenharmony_ci#endif
64353a5a1b3Sopenharmony_ci
64453a5a1b3Sopenharmony_ci    /* This function will be called from the main thread, before as
64553a5a1b3Sopenharmony_ci     * well as after the sink input has been activated using
64653a5a1b3Sopenharmony_ci     * pa_sink_input_put()! That means it may not touch any
64753a5a1b3Sopenharmony_ci     * ->thread_info data, such as the memblockq! */
64853a5a1b3Sopenharmony_ci
64953a5a1b3Sopenharmony_ci    frame_size = pa_frame_size(&s->sink_input->sample_spec);
65053a5a1b3Sopenharmony_ci    s->buffer_attr = s->buffer_attr_req;
65153a5a1b3Sopenharmony_ci
65253a5a1b3Sopenharmony_ci    if (s->buffer_attr.maxlength == (uint32_t) -1 || s->buffer_attr.maxlength > MAX_MEMBLOCKQ_LENGTH)
65353a5a1b3Sopenharmony_ci        s->buffer_attr.maxlength = MAX_MEMBLOCKQ_LENGTH;
65453a5a1b3Sopenharmony_ci    if (s->buffer_attr.maxlength <= 0)
65553a5a1b3Sopenharmony_ci        s->buffer_attr.maxlength = (uint32_t) frame_size;
65653a5a1b3Sopenharmony_ci
65753a5a1b3Sopenharmony_ci    if (s->buffer_attr.tlength == (uint32_t) -1)
65853a5a1b3Sopenharmony_ci        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_TLENGTH_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
65953a5a1b3Sopenharmony_ci    if (s->buffer_attr.tlength <= 0)
66053a5a1b3Sopenharmony_ci        s->buffer_attr.tlength = (uint32_t) frame_size;
66153a5a1b3Sopenharmony_ci    if (s->buffer_attr.tlength > s->buffer_attr.maxlength)
66253a5a1b3Sopenharmony_ci        s->buffer_attr.tlength = s->buffer_attr.maxlength;
66353a5a1b3Sopenharmony_ci
66453a5a1b3Sopenharmony_ci    if (s->buffer_attr.minreq == (uint32_t) -1) {
66553a5a1b3Sopenharmony_ci        uint32_t process = (uint32_t) pa_usec_to_bytes_round_up(DEFAULT_PROCESS_MSEC*PA_USEC_PER_MSEC, &s->sink_input->sample_spec);
66653a5a1b3Sopenharmony_ci        /* With low-latency, tlength/4 gives a decent default in all of traditional, adjust latency and early request modes. */
66753a5a1b3Sopenharmony_ci        uint32_t m = s->buffer_attr.tlength / 4;
66853a5a1b3Sopenharmony_ci        if (frame_size)
66953a5a1b3Sopenharmony_ci            m -= m % frame_size;
67053a5a1b3Sopenharmony_ci        s->buffer_attr.minreq = PA_MIN(process, m);
67153a5a1b3Sopenharmony_ci    }
67253a5a1b3Sopenharmony_ci    if (s->buffer_attr.minreq <= 0)
67353a5a1b3Sopenharmony_ci        s->buffer_attr.minreq = (uint32_t) frame_size;
67453a5a1b3Sopenharmony_ci
67553a5a1b3Sopenharmony_ci    if (s->buffer_attr.tlength < s->buffer_attr.minreq+frame_size)
67653a5a1b3Sopenharmony_ci        s->buffer_attr.tlength = s->buffer_attr.minreq+(uint32_t) frame_size;
67753a5a1b3Sopenharmony_ci
67853a5a1b3Sopenharmony_ci    orig_tlength_usec = tlength_usec = pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec);
67953a5a1b3Sopenharmony_ci    orig_minreq_usec = minreq_usec = pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec);
68053a5a1b3Sopenharmony_ci
68153a5a1b3Sopenharmony_ci    pa_log_info("Requested tlength=%0.2f ms, minreq=%0.2f ms",
68253a5a1b3Sopenharmony_ci                (double) tlength_usec / PA_USEC_PER_MSEC,
68353a5a1b3Sopenharmony_ci                (double) minreq_usec / PA_USEC_PER_MSEC);
68453a5a1b3Sopenharmony_ci
68553a5a1b3Sopenharmony_ci    if (s->early_requests) {
68653a5a1b3Sopenharmony_ci
68753a5a1b3Sopenharmony_ci        /* In early request mode we need to emulate the classic
68853a5a1b3Sopenharmony_ci         * fragment-based playback model. Unfortunately we have no
68953a5a1b3Sopenharmony_ci         * mechanism to tell the sink how often we want to be queried
69053a5a1b3Sopenharmony_ci         * for data. The next best thing we can do is to set the sink's
69153a5a1b3Sopenharmony_ci         * total buffer (i.e. its latency) to the fragment size. That
69253a5a1b3Sopenharmony_ci         * way it will have to query us at least that often. */
69353a5a1b3Sopenharmony_ci
69453a5a1b3Sopenharmony_ci        sink_usec = minreq_usec;
69553a5a1b3Sopenharmony_ci        pa_log_debug("Early requests mode enabled, configuring sink latency to minreq.");
69653a5a1b3Sopenharmony_ci
69753a5a1b3Sopenharmony_ci    } else if (s->adjust_latency) {
69853a5a1b3Sopenharmony_ci
69953a5a1b3Sopenharmony_ci        /* So, the user asked us to adjust the latency of the stream
70053a5a1b3Sopenharmony_ci         * buffer according to the what the sink can provide. The
70153a5a1b3Sopenharmony_ci         * tlength passed in shall be the overall latency. Roughly
70253a5a1b3Sopenharmony_ci         * half the latency will be spent on the hw buffer, the other
70353a5a1b3Sopenharmony_ci         * half of it in the async buffer queue we maintain for each
70453a5a1b3Sopenharmony_ci         * client. In between we'll have a safety space of size
70553a5a1b3Sopenharmony_ci         * 2*minreq. Why the 2*minreq? When the hw buffer is completely
70653a5a1b3Sopenharmony_ci         * empty and needs to be filled, then our buffer must have
70753a5a1b3Sopenharmony_ci         * enough data to fulfill this request immediately and thus
70853a5a1b3Sopenharmony_ci         * have at least the same tlength as the size of the hw
70953a5a1b3Sopenharmony_ci         * buffer. It additionally needs space for 2 times minreq
71053a5a1b3Sopenharmony_ci         * because if the buffer ran empty and a partial fillup
71153a5a1b3Sopenharmony_ci         * happens immediately on the next iteration we need to be
71253a5a1b3Sopenharmony_ci         * able to fulfill it and give the application also minreq
71353a5a1b3Sopenharmony_ci         * time to fill it up again for the next request Makes 2 times
71453a5a1b3Sopenharmony_ci         * minreq in plus.. */
71553a5a1b3Sopenharmony_ci
71653a5a1b3Sopenharmony_ci        if (tlength_usec > minreq_usec*2)
71753a5a1b3Sopenharmony_ci            sink_usec = (tlength_usec - minreq_usec*2)/2;
71853a5a1b3Sopenharmony_ci        else
71953a5a1b3Sopenharmony_ci            sink_usec = 0;
72053a5a1b3Sopenharmony_ci
72153a5a1b3Sopenharmony_ci        pa_log_debug("Adjust latency mode enabled, configuring sink latency to half of overall latency.");
72253a5a1b3Sopenharmony_ci
72353a5a1b3Sopenharmony_ci    } else {
72453a5a1b3Sopenharmony_ci
72553a5a1b3Sopenharmony_ci        /* Ok, the user didn't ask us to adjust the latency, but we
72653a5a1b3Sopenharmony_ci         * still need to make sure that the parameters from the user
72753a5a1b3Sopenharmony_ci         * do make sense. */
72853a5a1b3Sopenharmony_ci
72953a5a1b3Sopenharmony_ci        if (tlength_usec > minreq_usec*2)
73053a5a1b3Sopenharmony_ci            sink_usec = (tlength_usec - minreq_usec*2);
73153a5a1b3Sopenharmony_ci        else
73253a5a1b3Sopenharmony_ci            sink_usec = 0;
73353a5a1b3Sopenharmony_ci
73453a5a1b3Sopenharmony_ci        pa_log_debug("Traditional mode enabled, modifying sink usec only for compat with minreq.");
73553a5a1b3Sopenharmony_ci    }
73653a5a1b3Sopenharmony_ci
73753a5a1b3Sopenharmony_ci    s->configured_sink_latency = pa_sink_input_set_requested_latency(s->sink_input, sink_usec);
73853a5a1b3Sopenharmony_ci
73953a5a1b3Sopenharmony_ci    if (s->early_requests) {
74053a5a1b3Sopenharmony_ci
74153a5a1b3Sopenharmony_ci        /* Ok, we didn't necessarily get what we were asking for. We
74253a5a1b3Sopenharmony_ci         * might still get the proper fragment interval, we just can't
74353a5a1b3Sopenharmony_ci         * guarantee it. */
74453a5a1b3Sopenharmony_ci
74553a5a1b3Sopenharmony_ci        if (minreq_usec != s->configured_sink_latency)
74653a5a1b3Sopenharmony_ci            pa_log_debug("Could not configure a sufficiently low latency. Early requests might not be satisfied.");
74753a5a1b3Sopenharmony_ci
74853a5a1b3Sopenharmony_ci    } else if (s->adjust_latency) {
74953a5a1b3Sopenharmony_ci
75053a5a1b3Sopenharmony_ci        /* Ok, we didn't necessarily get what we were asking for, so
75153a5a1b3Sopenharmony_ci         * let's subtract from what we asked for for the remaining
75253a5a1b3Sopenharmony_ci         * buffer space */
75353a5a1b3Sopenharmony_ci
75453a5a1b3Sopenharmony_ci        if (tlength_usec >= s->configured_sink_latency)
75553a5a1b3Sopenharmony_ci            tlength_usec -= s->configured_sink_latency;
75653a5a1b3Sopenharmony_ci    }
75753a5a1b3Sopenharmony_ci
75853a5a1b3Sopenharmony_ci    pa_log_debug("Requested latency=%0.2f ms, Received latency=%0.2f ms",
75953a5a1b3Sopenharmony_ci                 (double) sink_usec / PA_USEC_PER_MSEC,
76053a5a1b3Sopenharmony_ci                 (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
76153a5a1b3Sopenharmony_ci
76253a5a1b3Sopenharmony_ci    /* FIXME: This is actually larger than necessary, since not all of
76353a5a1b3Sopenharmony_ci     * the sink latency is actually rewritable. */
76453a5a1b3Sopenharmony_ci    if (tlength_usec < s->configured_sink_latency + 2*minreq_usec)
76553a5a1b3Sopenharmony_ci        tlength_usec = s->configured_sink_latency + 2*minreq_usec;
76653a5a1b3Sopenharmony_ci
76753a5a1b3Sopenharmony_ci    if (pa_usec_to_bytes_round_up(orig_tlength_usec, &s->sink_input->sample_spec) !=
76853a5a1b3Sopenharmony_ci        pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec))
76953a5a1b3Sopenharmony_ci        s->buffer_attr.tlength = (uint32_t) pa_usec_to_bytes_round_up(tlength_usec, &s->sink_input->sample_spec);
77053a5a1b3Sopenharmony_ci
77153a5a1b3Sopenharmony_ci    if (pa_usec_to_bytes(orig_minreq_usec, &s->sink_input->sample_spec) !=
77253a5a1b3Sopenharmony_ci        pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec))
77353a5a1b3Sopenharmony_ci        s->buffer_attr.minreq = (uint32_t) pa_usec_to_bytes(minreq_usec, &s->sink_input->sample_spec);
77453a5a1b3Sopenharmony_ci
77553a5a1b3Sopenharmony_ci    if (s->buffer_attr.minreq <= 0) {
77653a5a1b3Sopenharmony_ci        s->buffer_attr.minreq = (uint32_t) frame_size;
77753a5a1b3Sopenharmony_ci        s->buffer_attr.tlength += (uint32_t) frame_size*2;
77853a5a1b3Sopenharmony_ci    }
77953a5a1b3Sopenharmony_ci
78053a5a1b3Sopenharmony_ci    if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
78153a5a1b3Sopenharmony_ci        s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
78253a5a1b3Sopenharmony_ci
78353a5a1b3Sopenharmony_ci    max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
78453a5a1b3Sopenharmony_ci
78553a5a1b3Sopenharmony_ci    if (s->buffer_attr.prebuf == (uint32_t) -1 ||
78653a5a1b3Sopenharmony_ci        s->buffer_attr.prebuf > max_prebuf)
78753a5a1b3Sopenharmony_ci        s->buffer_attr.prebuf = max_prebuf;
78853a5a1b3Sopenharmony_ci
78953a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
79053a5a1b3Sopenharmony_ci    pa_log("Client accepted: maxlength=%lu ms tlength=%lu ms minreq=%lu ms prebuf=%lu ms",
79153a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.maxlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
79253a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.tlength, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
79353a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.minreq, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC),
79453a5a1b3Sopenharmony_ci           (unsigned long) (pa_bytes_to_usec(s->buffer_attr.prebuf, &s->sink_input->sample_spec) / PA_USEC_PER_MSEC));
79553a5a1b3Sopenharmony_ci#endif
79653a5a1b3Sopenharmony_ci}
79753a5a1b3Sopenharmony_ci
79853a5a1b3Sopenharmony_ci/* Called from main context */
79953a5a1b3Sopenharmony_cistatic playback_stream* playback_stream_new(
80053a5a1b3Sopenharmony_ci        pa_native_connection *c,
80153a5a1b3Sopenharmony_ci        pa_sink *sink,
80253a5a1b3Sopenharmony_ci        pa_sample_spec *ss,
80353a5a1b3Sopenharmony_ci        pa_channel_map *map,
80453a5a1b3Sopenharmony_ci        pa_idxset *formats,
80553a5a1b3Sopenharmony_ci        pa_buffer_attr *a,
80653a5a1b3Sopenharmony_ci        pa_cvolume *volume,
80753a5a1b3Sopenharmony_ci        bool muted,
80853a5a1b3Sopenharmony_ci        bool muted_set,
80953a5a1b3Sopenharmony_ci        pa_sink_input_flags_t flags,
81053a5a1b3Sopenharmony_ci        pa_proplist *p,
81153a5a1b3Sopenharmony_ci        bool adjust_latency,
81253a5a1b3Sopenharmony_ci        bool early_requests,
81353a5a1b3Sopenharmony_ci        bool relative_volume,
81453a5a1b3Sopenharmony_ci        uint32_t syncid,
81553a5a1b3Sopenharmony_ci        uint32_t *missing,
81653a5a1b3Sopenharmony_ci        int *ret) {
81753a5a1b3Sopenharmony_ci
81853a5a1b3Sopenharmony_ci    /* Note: This function takes ownership of the 'formats' param, so we need
81953a5a1b3Sopenharmony_ci     * to take extra care to not leak it */
82053a5a1b3Sopenharmony_ci
82153a5a1b3Sopenharmony_ci    playback_stream *ssync;
82253a5a1b3Sopenharmony_ci    playback_stream *s = NULL;
82353a5a1b3Sopenharmony_ci    pa_sink_input *sink_input = NULL;
82453a5a1b3Sopenharmony_ci    pa_memchunk silence;
82553a5a1b3Sopenharmony_ci    uint32_t idx;
82653a5a1b3Sopenharmony_ci    int64_t start_index;
82753a5a1b3Sopenharmony_ci    pa_sink_input_new_data data;
82853a5a1b3Sopenharmony_ci    char *memblockq_name;
82953a5a1b3Sopenharmony_ci
83053a5a1b3Sopenharmony_ci    pa_assert(c);
83153a5a1b3Sopenharmony_ci    pa_assert(ss);
83253a5a1b3Sopenharmony_ci    pa_assert(missing);
83353a5a1b3Sopenharmony_ci    pa_assert(p);
83453a5a1b3Sopenharmony_ci    pa_assert(ret);
83553a5a1b3Sopenharmony_ci
83653a5a1b3Sopenharmony_ci    /* Find syncid group */
83753a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(ssync, c->output_streams, idx) {
83853a5a1b3Sopenharmony_ci
83953a5a1b3Sopenharmony_ci        if (!playback_stream_isinstance(ssync))
84053a5a1b3Sopenharmony_ci            continue;
84153a5a1b3Sopenharmony_ci
84253a5a1b3Sopenharmony_ci        if (ssync->syncid == syncid)
84353a5a1b3Sopenharmony_ci            break;
84453a5a1b3Sopenharmony_ci    }
84553a5a1b3Sopenharmony_ci
84653a5a1b3Sopenharmony_ci    /* Synced streams must connect to the same sink */
84753a5a1b3Sopenharmony_ci    if (ssync) {
84853a5a1b3Sopenharmony_ci
84953a5a1b3Sopenharmony_ci        if (!sink)
85053a5a1b3Sopenharmony_ci            sink = ssync->sink_input->sink;
85153a5a1b3Sopenharmony_ci        else if (sink != ssync->sink_input->sink) {
85253a5a1b3Sopenharmony_ci            *ret = PA_ERR_INVALID;
85353a5a1b3Sopenharmony_ci            goto out;
85453a5a1b3Sopenharmony_ci        }
85553a5a1b3Sopenharmony_ci    }
85653a5a1b3Sopenharmony_ci
85753a5a1b3Sopenharmony_ci    pa_sink_input_new_data_init(&data);
85853a5a1b3Sopenharmony_ci
85953a5a1b3Sopenharmony_ci    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, p);
86053a5a1b3Sopenharmony_ci    data.driver = __FILE__;
86153a5a1b3Sopenharmony_ci    data.module = c->options->module;
86253a5a1b3Sopenharmony_ci    data.client = c->client;
86353a5a1b3Sopenharmony_ci    if (sink)
86453a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_sink(&data, sink, false, true);
86553a5a1b3Sopenharmony_ci    if (pa_sample_spec_valid(ss))
86653a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_sample_spec(&data, ss);
86753a5a1b3Sopenharmony_ci    if (pa_channel_map_valid(map))
86853a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_channel_map(&data, map);
86953a5a1b3Sopenharmony_ci    if (formats) {
87053a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_formats(&data, formats);
87153a5a1b3Sopenharmony_ci        /* Ownership transferred to new_data, so we don't free it ourselves */
87253a5a1b3Sopenharmony_ci        formats = NULL;
87353a5a1b3Sopenharmony_ci    }
87453a5a1b3Sopenharmony_ci    if (volume) {
87553a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_volume(&data, volume);
87653a5a1b3Sopenharmony_ci        data.volume_is_absolute = !relative_volume;
87753a5a1b3Sopenharmony_ci        data.save_volume = false;
87853a5a1b3Sopenharmony_ci    }
87953a5a1b3Sopenharmony_ci    if (muted_set) {
88053a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_muted(&data, muted);
88153a5a1b3Sopenharmony_ci        data.save_muted = false;
88253a5a1b3Sopenharmony_ci    }
88353a5a1b3Sopenharmony_ci    data.sync_base = ssync ? ssync->sink_input : NULL;
88453a5a1b3Sopenharmony_ci    data.flags = flags;
88553a5a1b3Sopenharmony_ci
88653a5a1b3Sopenharmony_ci    *ret = -pa_sink_input_new(&sink_input, c->protocol->core, &data);
88753a5a1b3Sopenharmony_ci
88853a5a1b3Sopenharmony_ci    pa_sink_input_new_data_done(&data);
88953a5a1b3Sopenharmony_ci
89053a5a1b3Sopenharmony_ci    if (!sink_input)
89153a5a1b3Sopenharmony_ci        goto out;
89253a5a1b3Sopenharmony_ci
89353a5a1b3Sopenharmony_ci    s = pa_msgobject_new(playback_stream);
89453a5a1b3Sopenharmony_ci    s->parent.parent.parent.free = playback_stream_free;
89553a5a1b3Sopenharmony_ci    s->parent.parent.process_msg = playback_stream_process_msg;
89653a5a1b3Sopenharmony_ci    s->connection = c;
89753a5a1b3Sopenharmony_ci    s->syncid = syncid;
89853a5a1b3Sopenharmony_ci    s->sink_input = sink_input;
89953a5a1b3Sopenharmony_ci    s->is_underrun = true;
90053a5a1b3Sopenharmony_ci    s->drain_request = false;
90153a5a1b3Sopenharmony_ci    pa_atomic_store(&s->missing, 0);
90253a5a1b3Sopenharmony_ci    s->buffer_attr_req = *a;
90353a5a1b3Sopenharmony_ci    s->adjust_latency = adjust_latency;
90453a5a1b3Sopenharmony_ci    s->early_requests = early_requests;
90553a5a1b3Sopenharmony_ci    pa_atomic_store(&s->seek_or_post_in_queue, 0);
90653a5a1b3Sopenharmony_ci    s->seek_windex = -1;
90753a5a1b3Sopenharmony_ci
90853a5a1b3Sopenharmony_ci    s->sink_input->parent.process_msg = sink_input_process_msg;
90953a5a1b3Sopenharmony_ci    s->sink_input->pop = sink_input_pop_cb;
91053a5a1b3Sopenharmony_ci    s->sink_input->process_underrun = sink_input_process_underrun_cb;
91153a5a1b3Sopenharmony_ci    s->sink_input->process_rewind = sink_input_process_rewind_cb;
91253a5a1b3Sopenharmony_ci    s->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
91353a5a1b3Sopenharmony_ci    s->sink_input->update_max_request = sink_input_update_max_request_cb;
91453a5a1b3Sopenharmony_ci    s->sink_input->kill = sink_input_kill_cb;
91553a5a1b3Sopenharmony_ci    s->sink_input->moving = sink_input_moving_cb;
91653a5a1b3Sopenharmony_ci    s->sink_input->suspend = sink_input_suspend_cb;
91753a5a1b3Sopenharmony_ci    s->sink_input->send_event = sink_input_send_event_cb;
91853a5a1b3Sopenharmony_ci    s->sink_input->userdata = s;
91953a5a1b3Sopenharmony_ci
92053a5a1b3Sopenharmony_ci    s->sink_input->process_underrun_ohos = sink_input_process_underrun_ohos_cb;
92153a5a1b3Sopenharmony_ci
92253a5a1b3Sopenharmony_ci    start_index = ssync ? pa_memblockq_get_read_index(ssync->memblockq) : 0;
92353a5a1b3Sopenharmony_ci
92453a5a1b3Sopenharmony_ci    fix_playback_buffer_attr(s);
92553a5a1b3Sopenharmony_ci
92653a5a1b3Sopenharmony_ci    pa_sink_input_get_silence(sink_input, &silence);
92753a5a1b3Sopenharmony_ci    memblockq_name = pa_sprintf_malloc("native protocol playback stream memblockq [%u]", s->sink_input->index);
92853a5a1b3Sopenharmony_ci    s->memblockq = pa_memblockq_new(
92953a5a1b3Sopenharmony_ci            memblockq_name,
93053a5a1b3Sopenharmony_ci            start_index,
93153a5a1b3Sopenharmony_ci            s->buffer_attr.maxlength,
93253a5a1b3Sopenharmony_ci            s->buffer_attr.tlength,
93353a5a1b3Sopenharmony_ci            &sink_input->sample_spec,
93453a5a1b3Sopenharmony_ci            s->buffer_attr.prebuf,
93553a5a1b3Sopenharmony_ci            s->buffer_attr.minreq,
93653a5a1b3Sopenharmony_ci            0,
93753a5a1b3Sopenharmony_ci            &silence);
93853a5a1b3Sopenharmony_ci    pa_xfree(memblockq_name);
93953a5a1b3Sopenharmony_ci    pa_memblock_unref(silence.memblock);
94053a5a1b3Sopenharmony_ci
94153a5a1b3Sopenharmony_ci    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
94253a5a1b3Sopenharmony_ci
94353a5a1b3Sopenharmony_ci    *missing = (uint32_t) pa_memblockq_pop_missing(s->memblockq);
94453a5a1b3Sopenharmony_ci
94553a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
94653a5a1b3Sopenharmony_ci    pa_log("missing original: %li", (long int) *missing);
94753a5a1b3Sopenharmony_ci#endif
94853a5a1b3Sopenharmony_ci
94953a5a1b3Sopenharmony_ci    *ss = s->sink_input->sample_spec;
95053a5a1b3Sopenharmony_ci    *map = s->sink_input->channel_map;
95153a5a1b3Sopenharmony_ci
95253a5a1b3Sopenharmony_ci    pa_idxset_put(c->output_streams, s, &s->index);
95353a5a1b3Sopenharmony_ci
95453a5a1b3Sopenharmony_ci    pa_log_debug("Final latency %0.2f ms = %0.2f ms + 2*%0.2f ms + %0.2f ms",
95553a5a1b3Sopenharmony_ci                ((double) pa_bytes_to_usec(s->buffer_attr.tlength, &sink_input->sample_spec) + (double) s->configured_sink_latency) / PA_USEC_PER_MSEC,
95653a5a1b3Sopenharmony_ci                (double) pa_bytes_to_usec(s->buffer_attr.tlength-s->buffer_attr.minreq*2, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
95753a5a1b3Sopenharmony_ci                (double) pa_bytes_to_usec(s->buffer_attr.minreq, &sink_input->sample_spec) / PA_USEC_PER_MSEC,
95853a5a1b3Sopenharmony_ci                (double) s->configured_sink_latency / PA_USEC_PER_MSEC);
95953a5a1b3Sopenharmony_ci
96053a5a1b3Sopenharmony_ci    pa_sink_input_put(s->sink_input);
96153a5a1b3Sopenharmony_ci
96253a5a1b3Sopenharmony_ciout:
96353a5a1b3Sopenharmony_ci    if (formats)
96453a5a1b3Sopenharmony_ci        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
96553a5a1b3Sopenharmony_ci
96653a5a1b3Sopenharmony_ci    return s;
96753a5a1b3Sopenharmony_ci}
96853a5a1b3Sopenharmony_ci
96953a5a1b3Sopenharmony_ci/* Called from main context */
97053a5a1b3Sopenharmony_cistatic void playback_stream_send_killed(playback_stream *p) {
97153a5a1b3Sopenharmony_ci    pa_tagstruct *t;
97253a5a1b3Sopenharmony_ci    playback_stream_assert_ref(p);
97353a5a1b3Sopenharmony_ci
97453a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
97553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_KILLED);
97653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
97753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, p->index);
97853a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(p->connection->pstream, t);
97953a5a1b3Sopenharmony_ci}
98053a5a1b3Sopenharmony_ci
98153a5a1b3Sopenharmony_ci/* Called from main context */
98253a5a1b3Sopenharmony_cistatic int native_connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
98353a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(o);
98453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
98553a5a1b3Sopenharmony_ci
98653a5a1b3Sopenharmony_ci    if (!c->protocol)
98753a5a1b3Sopenharmony_ci        return -1;
98853a5a1b3Sopenharmony_ci
98953a5a1b3Sopenharmony_ci    switch (code) {
99053a5a1b3Sopenharmony_ci
99153a5a1b3Sopenharmony_ci        case CONNECTION_MESSAGE_REVOKE:
99253a5a1b3Sopenharmony_ci            pa_pstream_send_revoke(c->pstream, PA_PTR_TO_UINT(userdata));
99353a5a1b3Sopenharmony_ci            break;
99453a5a1b3Sopenharmony_ci
99553a5a1b3Sopenharmony_ci        case CONNECTION_MESSAGE_RELEASE:
99653a5a1b3Sopenharmony_ci            pa_pstream_send_release(c->pstream, PA_PTR_TO_UINT(userdata));
99753a5a1b3Sopenharmony_ci            break;
99853a5a1b3Sopenharmony_ci    }
99953a5a1b3Sopenharmony_ci
100053a5a1b3Sopenharmony_ci    return 0;
100153a5a1b3Sopenharmony_ci}
100253a5a1b3Sopenharmony_ci
100353a5a1b3Sopenharmony_ci/* Called from main context */
100453a5a1b3Sopenharmony_cistatic void native_connection_unlink(pa_native_connection *c) {
100553a5a1b3Sopenharmony_ci    record_stream *r;
100653a5a1b3Sopenharmony_ci    output_stream *o;
100753a5a1b3Sopenharmony_ci
100853a5a1b3Sopenharmony_ci    pa_assert(c);
100953a5a1b3Sopenharmony_ci
101053a5a1b3Sopenharmony_ci    if (!c->protocol)
101153a5a1b3Sopenharmony_ci        return;
101253a5a1b3Sopenharmony_ci
101353a5a1b3Sopenharmony_ci    pa_hook_fire(&c->protocol->hooks[PA_NATIVE_HOOK_CONNECTION_UNLINK], c);
101453a5a1b3Sopenharmony_ci
101553a5a1b3Sopenharmony_ci    if (c->options)
101653a5a1b3Sopenharmony_ci        pa_native_options_unref(c->options);
101753a5a1b3Sopenharmony_ci
101853a5a1b3Sopenharmony_ci    if (c->srbpending)
101953a5a1b3Sopenharmony_ci        pa_srbchannel_free(c->srbpending);
102053a5a1b3Sopenharmony_ci
102153a5a1b3Sopenharmony_ci    while ((r = pa_idxset_first(c->record_streams, NULL)))
102253a5a1b3Sopenharmony_ci        record_stream_unlink(r);
102353a5a1b3Sopenharmony_ci
102453a5a1b3Sopenharmony_ci    while ((o = pa_idxset_first(c->output_streams, NULL)))
102553a5a1b3Sopenharmony_ci        if (playback_stream_isinstance(o))
102653a5a1b3Sopenharmony_ci            playback_stream_unlink(PLAYBACK_STREAM(o));
102753a5a1b3Sopenharmony_ci        else
102853a5a1b3Sopenharmony_ci            upload_stream_unlink(UPLOAD_STREAM(o));
102953a5a1b3Sopenharmony_ci
103053a5a1b3Sopenharmony_ci    if (c->subscription)
103153a5a1b3Sopenharmony_ci        pa_subscription_free(c->subscription);
103253a5a1b3Sopenharmony_ci
103353a5a1b3Sopenharmony_ci    if (c->pstream)
103453a5a1b3Sopenharmony_ci        pa_pstream_unlink(c->pstream);
103553a5a1b3Sopenharmony_ci
103653a5a1b3Sopenharmony_ci    if (c->auth_timeout_event) {
103753a5a1b3Sopenharmony_ci        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
103853a5a1b3Sopenharmony_ci        c->auth_timeout_event = NULL;
103953a5a1b3Sopenharmony_ci    }
104053a5a1b3Sopenharmony_ci
104153a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
104253a5a1b3Sopenharmony_ci    c->protocol = NULL;
104353a5a1b3Sopenharmony_ci    pa_native_connection_unref(c);
104453a5a1b3Sopenharmony_ci}
104553a5a1b3Sopenharmony_ci
104653a5a1b3Sopenharmony_ci/* Called from main context */
104753a5a1b3Sopenharmony_cistatic void native_connection_free(pa_object *o) {
104853a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(o);
104953a5a1b3Sopenharmony_ci
105053a5a1b3Sopenharmony_ci    pa_assert(c);
105153a5a1b3Sopenharmony_ci
105253a5a1b3Sopenharmony_ci    native_connection_unlink(c);
105353a5a1b3Sopenharmony_ci
105453a5a1b3Sopenharmony_ci    pa_idxset_free(c->record_streams, NULL);
105553a5a1b3Sopenharmony_ci    pa_idxset_free(c->output_streams, NULL);
105653a5a1b3Sopenharmony_ci
105753a5a1b3Sopenharmony_ci    pa_pdispatch_unref(c->pdispatch);
105853a5a1b3Sopenharmony_ci    pa_pstream_unref(c->pstream);
105953a5a1b3Sopenharmony_ci    if (c->rw_mempool)
106053a5a1b3Sopenharmony_ci        pa_mempool_unref(c->rw_mempool);
106153a5a1b3Sopenharmony_ci
106253a5a1b3Sopenharmony_ci    pa_client_free(c->client);
106353a5a1b3Sopenharmony_ci
106453a5a1b3Sopenharmony_ci    pa_xfree(c);
106553a5a1b3Sopenharmony_ci}
106653a5a1b3Sopenharmony_ci
106753a5a1b3Sopenharmony_ci/* Called from main context */
106853a5a1b3Sopenharmony_cistatic void native_connection_send_memblock(pa_native_connection *c) {
106953a5a1b3Sopenharmony_ci    uint32_t start;
107053a5a1b3Sopenharmony_ci    record_stream *r;
107153a5a1b3Sopenharmony_ci
107253a5a1b3Sopenharmony_ci    start = PA_IDXSET_INVALID;
107353a5a1b3Sopenharmony_ci    for (;;) {
107453a5a1b3Sopenharmony_ci        pa_memchunk chunk;
107553a5a1b3Sopenharmony_ci
107653a5a1b3Sopenharmony_ci        if (!(r = RECORD_STREAM(pa_idxset_rrobin(c->record_streams, &c->rrobin_index))))
107753a5a1b3Sopenharmony_ci            return;
107853a5a1b3Sopenharmony_ci
107953a5a1b3Sopenharmony_ci        if (start == PA_IDXSET_INVALID)
108053a5a1b3Sopenharmony_ci            start = c->rrobin_index;
108153a5a1b3Sopenharmony_ci        else if (start == c->rrobin_index)
108253a5a1b3Sopenharmony_ci            return;
108353a5a1b3Sopenharmony_ci
108453a5a1b3Sopenharmony_ci        if (pa_memblockq_peek(r->memblockq, &chunk) >= 0) {
108553a5a1b3Sopenharmony_ci            pa_memchunk schunk = chunk;
108653a5a1b3Sopenharmony_ci
108753a5a1b3Sopenharmony_ci            if (schunk.length > r->buffer_attr.fragsize)
108853a5a1b3Sopenharmony_ci                schunk.length = r->buffer_attr.fragsize;
108953a5a1b3Sopenharmony_ci
109053a5a1b3Sopenharmony_ci            pa_pstream_send_memblock(c->pstream, r->index, 0, PA_SEEK_RELATIVE, &schunk);
109153a5a1b3Sopenharmony_ci
109253a5a1b3Sopenharmony_ci            pa_memblockq_drop(r->memblockq, schunk.length);
109353a5a1b3Sopenharmony_ci            pa_memblock_unref(schunk.memblock);
109453a5a1b3Sopenharmony_ci
109553a5a1b3Sopenharmony_ci            return;
109653a5a1b3Sopenharmony_ci        }
109753a5a1b3Sopenharmony_ci    }
109853a5a1b3Sopenharmony_ci}
109953a5a1b3Sopenharmony_ci
110053a5a1b3Sopenharmony_ci/*** sink input callbacks ***/
110153a5a1b3Sopenharmony_ci
110253a5a1b3Sopenharmony_ci/* Called from thread context */
110353a5a1b3Sopenharmony_cistatic void handle_seek(playback_stream *s, int64_t indexw) {
110453a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
110553a5a1b3Sopenharmony_ci
110653a5a1b3Sopenharmony_ci/*     pa_log("handle_seek: %llu -- %i", (unsigned long long) s->sink_input->thread_info.underrun_for, pa_memblockq_is_readable(s->memblockq)); */
110753a5a1b3Sopenharmony_ci
110853a5a1b3Sopenharmony_ci    if (s->sink_input->thread_info.underrun_for > 0) {
110953a5a1b3Sopenharmony_ci
111053a5a1b3Sopenharmony_ci/*         pa_log("%lu vs. %lu", (unsigned long) pa_memblockq_get_length(s->memblockq), (unsigned long) pa_memblockq_get_prebuf(s->memblockq)); */
111153a5a1b3Sopenharmony_ci
111253a5a1b3Sopenharmony_ci        if (pa_memblockq_is_readable(s->memblockq)) {
111353a5a1b3Sopenharmony_ci
111453a5a1b3Sopenharmony_ci            /* We just ended an underrun, let's ask the sink
111553a5a1b3Sopenharmony_ci             * for a complete rewind rewrite */
111653a5a1b3Sopenharmony_ci
111753a5a1b3Sopenharmony_ci            pa_log_debug("Requesting rewind due to end of underrun.");
111853a5a1b3Sopenharmony_ci            pa_sink_input_request_rewind(s->sink_input,
111953a5a1b3Sopenharmony_ci                                         (size_t) (s->sink_input->thread_info.underrun_for == (uint64_t) -1 ? 0 :
112053a5a1b3Sopenharmony_ci                                                   s->sink_input->thread_info.underrun_for),
112153a5a1b3Sopenharmony_ci                                         false, true, false);
112253a5a1b3Sopenharmony_ci        }
112353a5a1b3Sopenharmony_ci
112453a5a1b3Sopenharmony_ci    } else {
112553a5a1b3Sopenharmony_ci        int64_t indexr;
112653a5a1b3Sopenharmony_ci
112753a5a1b3Sopenharmony_ci        indexr = pa_memblockq_get_read_index(s->memblockq);
112853a5a1b3Sopenharmony_ci
112953a5a1b3Sopenharmony_ci        if (indexw < indexr) {
113053a5a1b3Sopenharmony_ci            /* OK, the sink already asked for this data, so
113153a5a1b3Sopenharmony_ci             * let's have it ask us again */
113253a5a1b3Sopenharmony_ci
113353a5a1b3Sopenharmony_ci            pa_log_debug("Requesting rewind due to rewrite.");
113453a5a1b3Sopenharmony_ci            pa_sink_input_request_rewind(s->sink_input, (size_t) (indexr - indexw), true, false, false);
113553a5a1b3Sopenharmony_ci        }
113653a5a1b3Sopenharmony_ci    }
113753a5a1b3Sopenharmony_ci
113853a5a1b3Sopenharmony_ci    playback_stream_request_bytes(s);
113953a5a1b3Sopenharmony_ci}
114053a5a1b3Sopenharmony_ci
114153a5a1b3Sopenharmony_cistatic void flush_write_no_account(pa_memblockq *q) {
114253a5a1b3Sopenharmony_ci    pa_memblockq_flush_write(q, false);
114353a5a1b3Sopenharmony_ci}
114453a5a1b3Sopenharmony_ci
114553a5a1b3Sopenharmony_ci/* Called from thread context */
114653a5a1b3Sopenharmony_cistatic int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
114753a5a1b3Sopenharmony_ci    pa_sink_input *i = PA_SINK_INPUT(o);
114853a5a1b3Sopenharmony_ci    playback_stream *s;
114953a5a1b3Sopenharmony_ci
115053a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
115153a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
115253a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
115353a5a1b3Sopenharmony_ci
115453a5a1b3Sopenharmony_ci    switch (code) {
115553a5a1b3Sopenharmony_ci
115653a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_SEEK:
115753a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_POST_DATA: {
115853a5a1b3Sopenharmony_ci            int64_t windex = pa_memblockq_get_write_index(s->memblockq);
115953a5a1b3Sopenharmony_ci
116053a5a1b3Sopenharmony_ci            if (code == SINK_INPUT_MESSAGE_SEEK) {
116153a5a1b3Sopenharmony_ci                /* The client side is incapable of accounting correctly
116253a5a1b3Sopenharmony_ci                 * for seeks of a type != PA_SEEK_RELATIVE. We need to be
116353a5a1b3Sopenharmony_ci                 * able to deal with that. */
116453a5a1b3Sopenharmony_ci
116553a5a1b3Sopenharmony_ci                pa_memblockq_seek(s->memblockq, offset, PA_PTR_TO_UINT(userdata), PA_PTR_TO_UINT(userdata) == PA_SEEK_RELATIVE);
116653a5a1b3Sopenharmony_ci                windex = PA_MIN(windex, pa_memblockq_get_write_index(s->memblockq));
116753a5a1b3Sopenharmony_ci            }
116853a5a1b3Sopenharmony_ci
116953a5a1b3Sopenharmony_ci            if (chunk && pa_memblockq_push_align(s->memblockq, chunk) < 0) {
117053a5a1b3Sopenharmony_ci                if (pa_log_ratelimit(PA_LOG_WARN))
117153a5a1b3Sopenharmony_ci                    pa_log_warn("Failed to push data into queue");
117253a5a1b3Sopenharmony_ci                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_OVERFLOW, NULL, 0, NULL, NULL);
117353a5a1b3Sopenharmony_ci                pa_memblockq_seek(s->memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, true);
117453a5a1b3Sopenharmony_ci            }
117553a5a1b3Sopenharmony_ci
117653a5a1b3Sopenharmony_ci            /* If more data is in queue, we rewind later instead. */
117753a5a1b3Sopenharmony_ci            if (s->seek_windex != -1)
117853a5a1b3Sopenharmony_ci                windex = PA_MIN(windex, s->seek_windex);
117953a5a1b3Sopenharmony_ci            if (pa_atomic_dec(&s->seek_or_post_in_queue) > 1)
118053a5a1b3Sopenharmony_ci                s->seek_windex = windex;
118153a5a1b3Sopenharmony_ci            else {
118253a5a1b3Sopenharmony_ci                s->seek_windex = -1;
118353a5a1b3Sopenharmony_ci                handle_seek(s, windex);
118453a5a1b3Sopenharmony_ci            }
118553a5a1b3Sopenharmony_ci            return 0;
118653a5a1b3Sopenharmony_ci        }
118753a5a1b3Sopenharmony_ci
118853a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_DRAIN:
118953a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_FLUSH:
119053a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_PREBUF_FORCE:
119153a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_TRIGGER: {
119253a5a1b3Sopenharmony_ci
119353a5a1b3Sopenharmony_ci            int64_t windex;
119453a5a1b3Sopenharmony_ci            pa_sink_input *isync;
119553a5a1b3Sopenharmony_ci            void (*func)(pa_memblockq *bq);
119653a5a1b3Sopenharmony_ci
119753a5a1b3Sopenharmony_ci            switch (code) {
119853a5a1b3Sopenharmony_ci                case SINK_INPUT_MESSAGE_FLUSH:
119953a5a1b3Sopenharmony_ci                    func = flush_write_no_account;
120053a5a1b3Sopenharmony_ci                    break;
120153a5a1b3Sopenharmony_ci
120253a5a1b3Sopenharmony_ci                case SINK_INPUT_MESSAGE_PREBUF_FORCE:
120353a5a1b3Sopenharmony_ci                    func = pa_memblockq_prebuf_force;
120453a5a1b3Sopenharmony_ci                    break;
120553a5a1b3Sopenharmony_ci
120653a5a1b3Sopenharmony_ci                case SINK_INPUT_MESSAGE_DRAIN:
120753a5a1b3Sopenharmony_ci                case SINK_INPUT_MESSAGE_TRIGGER:
120853a5a1b3Sopenharmony_ci                    func = pa_memblockq_prebuf_disable;
120953a5a1b3Sopenharmony_ci                    break;
121053a5a1b3Sopenharmony_ci
121153a5a1b3Sopenharmony_ci                default:
121253a5a1b3Sopenharmony_ci                    pa_assert_not_reached();
121353a5a1b3Sopenharmony_ci            }
121453a5a1b3Sopenharmony_ci
121553a5a1b3Sopenharmony_ci            windex = pa_memblockq_get_write_index(s->memblockq);
121653a5a1b3Sopenharmony_ci            func(s->memblockq);
121753a5a1b3Sopenharmony_ci            handle_seek(s, windex);
121853a5a1b3Sopenharmony_ci
121953a5a1b3Sopenharmony_ci            /* Do the same for all other members in the sync group */
122053a5a1b3Sopenharmony_ci            for (isync = i->sync_prev; isync; isync = isync->sync_prev) {
122153a5a1b3Sopenharmony_ci                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
122253a5a1b3Sopenharmony_ci                windex = pa_memblockq_get_write_index(ssync->memblockq);
122353a5a1b3Sopenharmony_ci                func(ssync->memblockq);
122453a5a1b3Sopenharmony_ci                handle_seek(ssync, windex);
122553a5a1b3Sopenharmony_ci            }
122653a5a1b3Sopenharmony_ci
122753a5a1b3Sopenharmony_ci            for (isync = i->sync_next; isync; isync = isync->sync_next) {
122853a5a1b3Sopenharmony_ci                playback_stream *ssync = PLAYBACK_STREAM(isync->userdata);
122953a5a1b3Sopenharmony_ci                windex = pa_memblockq_get_write_index(ssync->memblockq);
123053a5a1b3Sopenharmony_ci                func(ssync->memblockq);
123153a5a1b3Sopenharmony_ci                handle_seek(ssync, windex);
123253a5a1b3Sopenharmony_ci            }
123353a5a1b3Sopenharmony_ci
123453a5a1b3Sopenharmony_ci            if (code == SINK_INPUT_MESSAGE_DRAIN) {
123553a5a1b3Sopenharmony_ci                if (!pa_memblockq_is_readable(s->memblockq))
123653a5a1b3Sopenharmony_ci                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, userdata, 0, NULL, NULL);
123753a5a1b3Sopenharmony_ci                else {
123853a5a1b3Sopenharmony_ci                    s->drain_tag = PA_PTR_TO_UINT(userdata);
123953a5a1b3Sopenharmony_ci                    s->drain_request = true;
124053a5a1b3Sopenharmony_ci                }
124153a5a1b3Sopenharmony_ci            }
124253a5a1b3Sopenharmony_ci
124353a5a1b3Sopenharmony_ci            return 0;
124453a5a1b3Sopenharmony_ci        }
124553a5a1b3Sopenharmony_ci
124653a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_UPDATE_LATENCY:
124753a5a1b3Sopenharmony_ci            /* Atomically get a snapshot of all timing parameters... */
124853a5a1b3Sopenharmony_ci            s->read_index = pa_memblockq_get_read_index(s->memblockq);
124953a5a1b3Sopenharmony_ci            s->write_index = pa_memblockq_get_write_index(s->memblockq);
125053a5a1b3Sopenharmony_ci            s->render_memblockq_length = pa_memblockq_get_length(s->sink_input->thread_info.render_memblockq);
125153a5a1b3Sopenharmony_ci            s->current_sink_latency = pa_sink_get_latency_within_thread(s->sink_input->sink, false);
125253a5a1b3Sopenharmony_ci            /* Add resampler latency */
125353a5a1b3Sopenharmony_ci            s->current_sink_latency += pa_resampler_get_delay_usec(i->thread_info.resampler);
125453a5a1b3Sopenharmony_ci            s->underrun_for = s->sink_input->thread_info.underrun_for;
125553a5a1b3Sopenharmony_ci            s->playing_for = s->sink_input->thread_info.playing_for;
125653a5a1b3Sopenharmony_ci
125753a5a1b3Sopenharmony_ci            return 0;
125853a5a1b3Sopenharmony_ci
125953a5a1b3Sopenharmony_ci        case PA_SINK_INPUT_MESSAGE_SET_STATE: {
126053a5a1b3Sopenharmony_ci            int64_t windex;
126153a5a1b3Sopenharmony_ci
126253a5a1b3Sopenharmony_ci            windex = pa_memblockq_get_write_index(s->memblockq);
126353a5a1b3Sopenharmony_ci
126453a5a1b3Sopenharmony_ci            /* We enable prebuffering so that after CORKED -> RUNNING
126553a5a1b3Sopenharmony_ci             * transitions we don't have trouble with underruns in case the
126653a5a1b3Sopenharmony_ci             * buffer has too little data. This must not be done when draining
126753a5a1b3Sopenharmony_ci             * has been requested, however, otherwise the buffered audio would
126853a5a1b3Sopenharmony_ci             * never play. */
126953a5a1b3Sopenharmony_ci            if (!s->drain_request)
127053a5a1b3Sopenharmony_ci                pa_memblockq_prebuf_force(s->memblockq);
127153a5a1b3Sopenharmony_ci
127253a5a1b3Sopenharmony_ci            handle_seek(s, windex);
127353a5a1b3Sopenharmony_ci
127453a5a1b3Sopenharmony_ci            /* Fall through to the default handler */
127553a5a1b3Sopenharmony_ci            break;
127653a5a1b3Sopenharmony_ci        }
127753a5a1b3Sopenharmony_ci
127853a5a1b3Sopenharmony_ci        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
127953a5a1b3Sopenharmony_ci            pa_usec_t *r = userdata;
128053a5a1b3Sopenharmony_ci
128153a5a1b3Sopenharmony_ci            *r = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
128253a5a1b3Sopenharmony_ci
128353a5a1b3Sopenharmony_ci            /* Fall through, the default handler will add in the extra
128453a5a1b3Sopenharmony_ci             * latency added by the resampler */
128553a5a1b3Sopenharmony_ci            break;
128653a5a1b3Sopenharmony_ci        }
128753a5a1b3Sopenharmony_ci
128853a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR: {
128953a5a1b3Sopenharmony_ci            pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
129053a5a1b3Sopenharmony_ci            pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
129153a5a1b3Sopenharmony_ci            return 0;
129253a5a1b3Sopenharmony_ci        }
129353a5a1b3Sopenharmony_ci    }
129453a5a1b3Sopenharmony_ci
129553a5a1b3Sopenharmony_ci    return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
129653a5a1b3Sopenharmony_ci}
129753a5a1b3Sopenharmony_ci
129853a5a1b3Sopenharmony_cistatic bool handle_input_underrun(playback_stream *s, bool force) {
129953a5a1b3Sopenharmony_ci    bool send_drain;
130053a5a1b3Sopenharmony_ci
130153a5a1b3Sopenharmony_ci    if (pa_memblockq_is_readable(s->memblockq))
130253a5a1b3Sopenharmony_ci        return false;
130353a5a1b3Sopenharmony_ci
130453a5a1b3Sopenharmony_ci    if (!s->is_underrun)
130553a5a1b3Sopenharmony_ci        pa_log_debug("%s %s of '%s'", force ? "Actual" : "Implicit",
130653a5a1b3Sopenharmony_ci            s->drain_request ? "drain" : "underrun", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
130753a5a1b3Sopenharmony_ci
130853a5a1b3Sopenharmony_ci    send_drain = s->drain_request && (force || pa_sink_input_safe_to_remove(s->sink_input));
130953a5a1b3Sopenharmony_ci
131053a5a1b3Sopenharmony_ci    if (send_drain) {
131153a5a1b3Sopenharmony_ci         s->drain_request = false;
131253a5a1b3Sopenharmony_ci         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_DRAIN_ACK, PA_UINT_TO_PTR(s->drain_tag), 0, NULL, NULL);
131353a5a1b3Sopenharmony_ci         pa_log_debug("Drain acknowledged of '%s'", pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
131453a5a1b3Sopenharmony_ci    } else if (!s->is_underrun) {
131553a5a1b3Sopenharmony_ci         pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW, NULL, pa_memblockq_get_read_index(s->memblockq), NULL, NULL);
131653a5a1b3Sopenharmony_ci    }
131753a5a1b3Sopenharmony_ci    s->is_underrun = true;
131853a5a1b3Sopenharmony_ci    playback_stream_request_bytes(s);
131953a5a1b3Sopenharmony_ci    return true;
132053a5a1b3Sopenharmony_ci}
132153a5a1b3Sopenharmony_ci
132253a5a1b3Sopenharmony_ci/* Called from thread context */
132353a5a1b3Sopenharmony_cistatic bool sink_input_process_underrun_cb(pa_sink_input *i) {
132453a5a1b3Sopenharmony_ci    playback_stream *s;
132553a5a1b3Sopenharmony_ci
132653a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
132753a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
132853a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
132953a5a1b3Sopenharmony_ci
133053a5a1b3Sopenharmony_ci    return handle_input_underrun(s, true);
133153a5a1b3Sopenharmony_ci}
133253a5a1b3Sopenharmony_ci
133353a5a1b3Sopenharmony_cistatic bool handle_input_underrun_ohos(playback_stream *s, bool force) {
133453a5a1b3Sopenharmony_ci    bool send_drain;
133553a5a1b3Sopenharmony_ci
133653a5a1b3Sopenharmony_ci    if (pa_memblockq_is_readable(s->memblockq))
133753a5a1b3Sopenharmony_ci        return false;
133853a5a1b3Sopenharmony_ci
133953a5a1b3Sopenharmony_ci    if (!s->is_underrun)
134053a5a1b3Sopenharmony_ci        pa_log_debug("%s %s of '%s'", force ? "Actual" : "Implicit",
134153a5a1b3Sopenharmony_ci            s->drain_request ? "drain" : "underrun",
134253a5a1b3Sopenharmony_ci            pa_strnull(pa_proplist_gets(s->sink_input->proplist, PA_PROP_MEDIA_NAME)));
134353a5a1b3Sopenharmony_ci
134453a5a1b3Sopenharmony_ci    send_drain = s->drain_request && (force || pa_sink_input_safe_to_remove(s->sink_input));
134553a5a1b3Sopenharmony_ci    if (!send_drain) {
134653a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UNDERFLOW_OHOS,
134753a5a1b3Sopenharmony_ci            NULL, pa_memblockq_get_read_index(s->memblockq), NULL, NULL);
134853a5a1b3Sopenharmony_ci    }
134953a5a1b3Sopenharmony_ci    return true;
135053a5a1b3Sopenharmony_ci}
135153a5a1b3Sopenharmony_ci
135253a5a1b3Sopenharmony_cistatic bool sink_input_process_underrun_ohos_cb(pa_sink_input *i) {
135353a5a1b3Sopenharmony_ci    playback_stream *s;
135453a5a1b3Sopenharmony_ci
135553a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
135653a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
135753a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
135853a5a1b3Sopenharmony_ci
135953a5a1b3Sopenharmony_ci    return handle_input_underrun_ohos(s, true);
136053a5a1b3Sopenharmony_ci}
136153a5a1b3Sopenharmony_ci
136253a5a1b3Sopenharmony_ci/* Called from thread context */
136353a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) {
136453a5a1b3Sopenharmony_ci    playback_stream *s;
136553a5a1b3Sopenharmony_ci
136653a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
136753a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
136853a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
136953a5a1b3Sopenharmony_ci    pa_assert(chunk);
137053a5a1b3Sopenharmony_ci
137153a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
137253a5a1b3Sopenharmony_ci    pa_log("%s, pop(): %lu", pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME), (unsigned long) pa_memblockq_get_length(s->memblockq));
137353a5a1b3Sopenharmony_ci#endif
137453a5a1b3Sopenharmony_ci
137553a5a1b3Sopenharmony_ci    if (!handle_input_underrun(s, false))
137653a5a1b3Sopenharmony_ci        s->is_underrun = false;
137753a5a1b3Sopenharmony_ci
137853a5a1b3Sopenharmony_ci    /* This call will not fail with prebuf=0, hence we check for
137953a5a1b3Sopenharmony_ci       underrun explicitly in handle_input_underrun */
138053a5a1b3Sopenharmony_ci    if (pa_memblockq_peek(s->memblockq, chunk) < 0)
138153a5a1b3Sopenharmony_ci        return -1;
138253a5a1b3Sopenharmony_ci
138353a5a1b3Sopenharmony_ci    chunk->length = PA_MIN(nbytes, chunk->length);
138453a5a1b3Sopenharmony_ci
138553a5a1b3Sopenharmony_ci    if (i->thread_info.underrun_for > 0)
138653a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_STARTED, NULL, 0, NULL, NULL);
138753a5a1b3Sopenharmony_ci
138853a5a1b3Sopenharmony_ci    pa_memblockq_drop(s->memblockq, chunk->length);
138953a5a1b3Sopenharmony_ci    playback_stream_request_bytes(s);
139053a5a1b3Sopenharmony_ci
139153a5a1b3Sopenharmony_ci    return 0;
139253a5a1b3Sopenharmony_ci}
139353a5a1b3Sopenharmony_ci
139453a5a1b3Sopenharmony_ci/* Called from thread context */
139553a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
139653a5a1b3Sopenharmony_ci    playback_stream *s;
139753a5a1b3Sopenharmony_ci
139853a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
139953a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
140053a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
140153a5a1b3Sopenharmony_ci
140253a5a1b3Sopenharmony_ci    /* If we are in an underrun, then we don't rewind */
140353a5a1b3Sopenharmony_ci    if (i->thread_info.underrun_for > 0)
140453a5a1b3Sopenharmony_ci        return;
140553a5a1b3Sopenharmony_ci
140653a5a1b3Sopenharmony_ci    pa_memblockq_rewind(s->memblockq, nbytes);
140753a5a1b3Sopenharmony_ci}
140853a5a1b3Sopenharmony_ci
140953a5a1b3Sopenharmony_ci/* Called from thread context */
141053a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
141153a5a1b3Sopenharmony_ci    playback_stream *s;
141253a5a1b3Sopenharmony_ci
141353a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
141453a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
141553a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
141653a5a1b3Sopenharmony_ci
141753a5a1b3Sopenharmony_ci    pa_memblockq_set_maxrewind(s->memblockq, nbytes);
141853a5a1b3Sopenharmony_ci}
141953a5a1b3Sopenharmony_ci
142053a5a1b3Sopenharmony_ci/* Called from thread context */
142153a5a1b3Sopenharmony_cistatic void sink_input_update_max_request_cb(pa_sink_input *i, size_t nbytes) {
142253a5a1b3Sopenharmony_ci    playback_stream *s;
142353a5a1b3Sopenharmony_ci    size_t new_tlength, old_tlength;
142453a5a1b3Sopenharmony_ci
142553a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
142653a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
142753a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
142853a5a1b3Sopenharmony_ci
142953a5a1b3Sopenharmony_ci    old_tlength = pa_memblockq_get_tlength(s->memblockq);
143053a5a1b3Sopenharmony_ci    new_tlength = nbytes+2*pa_memblockq_get_minreq(s->memblockq);
143153a5a1b3Sopenharmony_ci
143253a5a1b3Sopenharmony_ci    if (old_tlength < new_tlength) {
143353a5a1b3Sopenharmony_ci        pa_log_debug("max_request changed, trying to update from %zu to %zu.", old_tlength,
143453a5a1b3Sopenharmony_ci            new_tlength);
143553a5a1b3Sopenharmony_ci        pa_memblockq_set_tlength(s->memblockq, new_tlength);
143653a5a1b3Sopenharmony_ci        new_tlength = pa_memblockq_get_tlength(s->memblockq);
143753a5a1b3Sopenharmony_ci
143853a5a1b3Sopenharmony_ci        if (new_tlength == old_tlength)
143953a5a1b3Sopenharmony_ci            pa_log_debug("Failed to increase tlength");
144053a5a1b3Sopenharmony_ci        else {
144153a5a1b3Sopenharmony_ci            pa_log_debug("Notifying client about increased tlength");
144253a5a1b3Sopenharmony_ci            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), PLAYBACK_STREAM_MESSAGE_UPDATE_TLENGTH, NULL, pa_memblockq_get_tlength(s->memblockq), NULL, NULL);
144353a5a1b3Sopenharmony_ci        }
144453a5a1b3Sopenharmony_ci    }
144553a5a1b3Sopenharmony_ci}
144653a5a1b3Sopenharmony_ci
144753a5a1b3Sopenharmony_ci/* Called from main context */
144853a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) {
144953a5a1b3Sopenharmony_ci    playback_stream *s;
145053a5a1b3Sopenharmony_ci
145153a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
145253a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
145353a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
145453a5a1b3Sopenharmony_ci
145553a5a1b3Sopenharmony_ci    playback_stream_send_killed(s);
145653a5a1b3Sopenharmony_ci    playback_stream_unlink(s);
145753a5a1b3Sopenharmony_ci}
145853a5a1b3Sopenharmony_ci
145953a5a1b3Sopenharmony_ci/* Called from main context */
146053a5a1b3Sopenharmony_cistatic void sink_input_send_event_cb(pa_sink_input *i, const char *event, pa_proplist *pl) {
146153a5a1b3Sopenharmony_ci    playback_stream *s;
146253a5a1b3Sopenharmony_ci    pa_tagstruct *t;
146353a5a1b3Sopenharmony_ci
146453a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
146553a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
146653a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
146753a5a1b3Sopenharmony_ci
146853a5a1b3Sopenharmony_ci    if (s->connection->version < 15)
146953a5a1b3Sopenharmony_ci      return;
147053a5a1b3Sopenharmony_ci
147153a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
147253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_EVENT);
147353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
147453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
147553a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, event);
147653a5a1b3Sopenharmony_ci    pa_tagstruct_put_proplist(t, pl);
147753a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(s->connection->pstream, t);
147853a5a1b3Sopenharmony_ci}
147953a5a1b3Sopenharmony_ci
148053a5a1b3Sopenharmony_ci/* Called from main context */
148153a5a1b3Sopenharmony_cistatic void sink_input_suspend_cb(pa_sink_input *i, pa_sink_state_t old_state, pa_suspend_cause_t old_suspend_cause) {
148253a5a1b3Sopenharmony_ci    playback_stream *s;
148353a5a1b3Sopenharmony_ci    pa_tagstruct *t;
148453a5a1b3Sopenharmony_ci    bool suspend;
148553a5a1b3Sopenharmony_ci
148653a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
148753a5a1b3Sopenharmony_ci
148853a5a1b3Sopenharmony_ci    /* State has not changed, nothing to do */
148953a5a1b3Sopenharmony_ci    if (old_state == i->sink->state)
149053a5a1b3Sopenharmony_ci        return;
149153a5a1b3Sopenharmony_ci
149253a5a1b3Sopenharmony_ci    suspend = (i->sink->state == PA_SINK_SUSPENDED);
149353a5a1b3Sopenharmony_ci
149453a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
149553a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
149653a5a1b3Sopenharmony_ci
149753a5a1b3Sopenharmony_ci    if (s->connection->version < 12)
149853a5a1b3Sopenharmony_ci      return;
149953a5a1b3Sopenharmony_ci
150053a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
150153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_SUSPENDED);
150253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
150353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
150453a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, suspend);
150553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(s->connection->pstream, t);
150653a5a1b3Sopenharmony_ci}
150753a5a1b3Sopenharmony_ci
150853a5a1b3Sopenharmony_ci/* Called from main context */
150953a5a1b3Sopenharmony_cistatic void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) {
151053a5a1b3Sopenharmony_ci    playback_stream *s;
151153a5a1b3Sopenharmony_ci    pa_tagstruct *t;
151253a5a1b3Sopenharmony_ci
151353a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
151453a5a1b3Sopenharmony_ci    s = PLAYBACK_STREAM(i->userdata);
151553a5a1b3Sopenharmony_ci    playback_stream_assert_ref(s);
151653a5a1b3Sopenharmony_ci
151753a5a1b3Sopenharmony_ci    if (!dest)
151853a5a1b3Sopenharmony_ci        return;
151953a5a1b3Sopenharmony_ci
152053a5a1b3Sopenharmony_ci    fix_playback_buffer_attr(s);
152153a5a1b3Sopenharmony_ci    pa_memblockq_apply_attr(s->memblockq, &s->buffer_attr);
152253a5a1b3Sopenharmony_ci    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
152353a5a1b3Sopenharmony_ci
152453a5a1b3Sopenharmony_ci    if (s->connection->version < 12)
152553a5a1b3Sopenharmony_ci      return;
152653a5a1b3Sopenharmony_ci
152753a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
152853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_PLAYBACK_STREAM_MOVED);
152953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
153053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
153153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, dest->index);
153253a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, dest->name);
153353a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, dest->state == PA_SINK_SUSPENDED);
153453a5a1b3Sopenharmony_ci
153553a5a1b3Sopenharmony_ci    if (s->connection->version >= 13) {
153653a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
153753a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, s->buffer_attr.tlength);
153853a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, s->buffer_attr.prebuf);
153953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, s->buffer_attr.minreq);
154053a5a1b3Sopenharmony_ci        pa_tagstruct_put_usec(t, s->configured_sink_latency);
154153a5a1b3Sopenharmony_ci    }
154253a5a1b3Sopenharmony_ci
154353a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(s->connection->pstream, t);
154453a5a1b3Sopenharmony_ci}
154553a5a1b3Sopenharmony_ci
154653a5a1b3Sopenharmony_ci/*** source_output callbacks ***/
154753a5a1b3Sopenharmony_ci
154853a5a1b3Sopenharmony_ci/* Called from thread context */
154953a5a1b3Sopenharmony_cistatic int source_output_process_msg(pa_msgobject *_o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
155053a5a1b3Sopenharmony_ci    pa_source_output *o = PA_SOURCE_OUTPUT(_o);
155153a5a1b3Sopenharmony_ci    record_stream *s;
155253a5a1b3Sopenharmony_ci
155353a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
155453a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
155553a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
155653a5a1b3Sopenharmony_ci
155753a5a1b3Sopenharmony_ci    switch (code) {
155853a5a1b3Sopenharmony_ci        case SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY:
155953a5a1b3Sopenharmony_ci            /* Atomically get a snapshot of all timing parameters... */
156053a5a1b3Sopenharmony_ci            s->current_monitor_latency = o->source->monitor_of ? pa_sink_get_latency_within_thread(o->source->monitor_of, false) : 0;
156153a5a1b3Sopenharmony_ci            s->current_source_latency = pa_source_get_latency_within_thread(o->source, false);
156253a5a1b3Sopenharmony_ci            /* Add resampler latency */
156353a5a1b3Sopenharmony_ci            s->current_source_latency += pa_resampler_get_delay_usec(o->thread_info.resampler);
156453a5a1b3Sopenharmony_ci            s->on_the_fly_snapshot = pa_atomic_load(&s->on_the_fly);
156553a5a1b3Sopenharmony_ci            return 0;
156653a5a1b3Sopenharmony_ci    }
156753a5a1b3Sopenharmony_ci
156853a5a1b3Sopenharmony_ci    return pa_source_output_process_msg(_o, code, userdata, offset, chunk);
156953a5a1b3Sopenharmony_ci}
157053a5a1b3Sopenharmony_ci
157153a5a1b3Sopenharmony_ci/* Called from thread context */
157253a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
157353a5a1b3Sopenharmony_ci    record_stream *s;
157453a5a1b3Sopenharmony_ci
157553a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
157653a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
157753a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
157853a5a1b3Sopenharmony_ci    pa_assert(chunk);
157953a5a1b3Sopenharmony_ci
158053a5a1b3Sopenharmony_ci    pa_atomic_add(&s->on_the_fly, chunk->length);
158153a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), RECORD_STREAM_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
158253a5a1b3Sopenharmony_ci}
158353a5a1b3Sopenharmony_ci
158453a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o) {
158553a5a1b3Sopenharmony_ci    record_stream *s;
158653a5a1b3Sopenharmony_ci
158753a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
158853a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
158953a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
159053a5a1b3Sopenharmony_ci
159153a5a1b3Sopenharmony_ci    record_stream_send_killed(s);
159253a5a1b3Sopenharmony_ci    record_stream_unlink(s);
159353a5a1b3Sopenharmony_ci}
159453a5a1b3Sopenharmony_ci
159553a5a1b3Sopenharmony_cistatic pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
159653a5a1b3Sopenharmony_ci    record_stream *s;
159753a5a1b3Sopenharmony_ci
159853a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
159953a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
160053a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
160153a5a1b3Sopenharmony_ci
160253a5a1b3Sopenharmony_ci    /*pa_log("get_latency: %u", pa_memblockq_get_length(s->memblockq));*/
160353a5a1b3Sopenharmony_ci
160453a5a1b3Sopenharmony_ci    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &o->sample_spec);
160553a5a1b3Sopenharmony_ci}
160653a5a1b3Sopenharmony_ci
160753a5a1b3Sopenharmony_ci/* Called from main context */
160853a5a1b3Sopenharmony_cistatic void source_output_send_event_cb(pa_source_output *o, const char *event, pa_proplist *pl) {
160953a5a1b3Sopenharmony_ci    record_stream *s;
161053a5a1b3Sopenharmony_ci    pa_tagstruct *t;
161153a5a1b3Sopenharmony_ci
161253a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
161353a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
161453a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
161553a5a1b3Sopenharmony_ci
161653a5a1b3Sopenharmony_ci    if (s->connection->version < 15)
161753a5a1b3Sopenharmony_ci      return;
161853a5a1b3Sopenharmony_ci
161953a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
162053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_EVENT);
162153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
162253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
162353a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, event);
162453a5a1b3Sopenharmony_ci    pa_tagstruct_put_proplist(t, pl);
162553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(s->connection->pstream, t);
162653a5a1b3Sopenharmony_ci}
162753a5a1b3Sopenharmony_ci
162853a5a1b3Sopenharmony_ci/* Called from main context */
162953a5a1b3Sopenharmony_cistatic void source_output_suspend_cb(pa_source_output *o, pa_source_state_t old_state, pa_suspend_cause_t old_suspend_cause) {
163053a5a1b3Sopenharmony_ci    record_stream *s;
163153a5a1b3Sopenharmony_ci    pa_tagstruct *t;
163253a5a1b3Sopenharmony_ci    bool suspend;
163353a5a1b3Sopenharmony_ci
163453a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
163553a5a1b3Sopenharmony_ci
163653a5a1b3Sopenharmony_ci    /* State has not changed, nothing to do */
163753a5a1b3Sopenharmony_ci    if (old_state == o->source->state)
163853a5a1b3Sopenharmony_ci        return;
163953a5a1b3Sopenharmony_ci
164053a5a1b3Sopenharmony_ci    suspend = (o->source->state == PA_SOURCE_SUSPENDED);
164153a5a1b3Sopenharmony_ci
164253a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
164353a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
164453a5a1b3Sopenharmony_ci
164553a5a1b3Sopenharmony_ci    if (s->connection->version < 12)
164653a5a1b3Sopenharmony_ci      return;
164753a5a1b3Sopenharmony_ci
164853a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
164953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_SUSPENDED);
165053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
165153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
165253a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, suspend);
165353a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(s->connection->pstream, t);
165453a5a1b3Sopenharmony_ci}
165553a5a1b3Sopenharmony_ci
165653a5a1b3Sopenharmony_ci/* Called from main context */
165753a5a1b3Sopenharmony_cistatic void source_output_moving_cb(pa_source_output *o, pa_source *dest) {
165853a5a1b3Sopenharmony_ci    record_stream *s;
165953a5a1b3Sopenharmony_ci    pa_tagstruct *t;
166053a5a1b3Sopenharmony_ci
166153a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
166253a5a1b3Sopenharmony_ci    s = RECORD_STREAM(o->userdata);
166353a5a1b3Sopenharmony_ci    record_stream_assert_ref(s);
166453a5a1b3Sopenharmony_ci
166553a5a1b3Sopenharmony_ci    if (!dest)
166653a5a1b3Sopenharmony_ci        return;
166753a5a1b3Sopenharmony_ci
166853a5a1b3Sopenharmony_ci    fix_record_buffer_attr_pre(s);
166953a5a1b3Sopenharmony_ci    pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
167053a5a1b3Sopenharmony_ci    pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
167153a5a1b3Sopenharmony_ci    fix_record_buffer_attr_post(s);
167253a5a1b3Sopenharmony_ci
167353a5a1b3Sopenharmony_ci    if (s->connection->version < 12)
167453a5a1b3Sopenharmony_ci      return;
167553a5a1b3Sopenharmony_ci
167653a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
167753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_RECORD_STREAM_MOVED);
167853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
167953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
168053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, dest->index);
168153a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, dest->name);
168253a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, dest->state == PA_SOURCE_SUSPENDED);
168353a5a1b3Sopenharmony_ci
168453a5a1b3Sopenharmony_ci    if (s->connection->version >= 13) {
168553a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, s->buffer_attr.maxlength);
168653a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, s->buffer_attr.fragsize);
168753a5a1b3Sopenharmony_ci        pa_tagstruct_put_usec(t, s->configured_source_latency);
168853a5a1b3Sopenharmony_ci    }
168953a5a1b3Sopenharmony_ci
169053a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(s->connection->pstream, t);
169153a5a1b3Sopenharmony_ci}
169253a5a1b3Sopenharmony_ci
169353a5a1b3Sopenharmony_ci/*** pdispatch callbacks ***/
169453a5a1b3Sopenharmony_ci
169553a5a1b3Sopenharmony_cistatic void protocol_error(pa_native_connection *c) {
169653a5a1b3Sopenharmony_ci    pa_log("protocol error, kicking client");
169753a5a1b3Sopenharmony_ci    native_connection_unlink(c);
169853a5a1b3Sopenharmony_ci}
169953a5a1b3Sopenharmony_ci
170053a5a1b3Sopenharmony_ci#define CHECK_VALIDITY(pstream, expression, tag, error) do { \
170153a5a1b3Sopenharmony_ciif (!(expression)) { \
170253a5a1b3Sopenharmony_ci    pa_pstream_send_error((pstream), (tag), (error)); \
170353a5a1b3Sopenharmony_ci    return; \
170453a5a1b3Sopenharmony_ci} \
170553a5a1b3Sopenharmony_ci} while(0);
170653a5a1b3Sopenharmony_ci
170753a5a1b3Sopenharmony_ci#define CHECK_VALIDITY_GOTO(pstream, expression, tag, error, label) do { \
170853a5a1b3Sopenharmony_ciif (!(expression)) { \
170953a5a1b3Sopenharmony_ci    pa_pstream_send_error((pstream), (tag), (error)); \
171053a5a1b3Sopenharmony_ci    goto label; \
171153a5a1b3Sopenharmony_ci} \
171253a5a1b3Sopenharmony_ci} while(0);
171353a5a1b3Sopenharmony_ci
171453a5a1b3Sopenharmony_cistatic pa_tagstruct *reply_new(uint32_t tag) {
171553a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
171653a5a1b3Sopenharmony_ci
171753a5a1b3Sopenharmony_ci    reply = pa_tagstruct_new();
171853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
171953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, tag);
172053a5a1b3Sopenharmony_ci    return reply;
172153a5a1b3Sopenharmony_ci}
172253a5a1b3Sopenharmony_ci
172353a5a1b3Sopenharmony_cistatic void command_create_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
172453a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
172553a5a1b3Sopenharmony_ci    playback_stream *s;
172653a5a1b3Sopenharmony_ci    uint32_t sink_index, syncid, missing = 0;
172753a5a1b3Sopenharmony_ci    pa_buffer_attr attr;
172853a5a1b3Sopenharmony_ci    const char *name = NULL, *sink_name;
172953a5a1b3Sopenharmony_ci    pa_sample_spec ss;
173053a5a1b3Sopenharmony_ci    pa_channel_map map;
173153a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
173253a5a1b3Sopenharmony_ci    pa_sink *sink = NULL;
173353a5a1b3Sopenharmony_ci    pa_cvolume volume;
173453a5a1b3Sopenharmony_ci    bool
173553a5a1b3Sopenharmony_ci        corked = false,
173653a5a1b3Sopenharmony_ci        no_remap = false,
173753a5a1b3Sopenharmony_ci        no_remix = false,
173853a5a1b3Sopenharmony_ci        fix_format = false,
173953a5a1b3Sopenharmony_ci        fix_rate = false,
174053a5a1b3Sopenharmony_ci        fix_channels = false,
174153a5a1b3Sopenharmony_ci        no_move = false,
174253a5a1b3Sopenharmony_ci        variable_rate = false,
174353a5a1b3Sopenharmony_ci        muted = false,
174453a5a1b3Sopenharmony_ci        adjust_latency = false,
174553a5a1b3Sopenharmony_ci        early_requests = false,
174653a5a1b3Sopenharmony_ci        dont_inhibit_auto_suspend = false,
174753a5a1b3Sopenharmony_ci        volume_set = true,
174853a5a1b3Sopenharmony_ci        muted_set = false,
174953a5a1b3Sopenharmony_ci        fail_on_suspend = false,
175053a5a1b3Sopenharmony_ci        relative_volume = false,
175153a5a1b3Sopenharmony_ci        passthrough = false;
175253a5a1b3Sopenharmony_ci
175353a5a1b3Sopenharmony_ci    pa_sink_input_flags_t flags = 0;
175453a5a1b3Sopenharmony_ci    pa_proplist *p = NULL;
175553a5a1b3Sopenharmony_ci    int ret = PA_ERR_INVALID;
175653a5a1b3Sopenharmony_ci    uint8_t n_formats = 0;
175753a5a1b3Sopenharmony_ci    pa_format_info *format;
175853a5a1b3Sopenharmony_ci    pa_idxset *formats = NULL;
175953a5a1b3Sopenharmony_ci    uint32_t i;
176053a5a1b3Sopenharmony_ci
176153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
176253a5a1b3Sopenharmony_ci    pa_assert(t);
176353a5a1b3Sopenharmony_ci    memset(&attr, 0, sizeof(attr));
176453a5a1b3Sopenharmony_ci
176553a5a1b3Sopenharmony_ci    if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
176653a5a1b3Sopenharmony_ci        pa_tagstruct_get(
176753a5a1b3Sopenharmony_ci                t,
176853a5a1b3Sopenharmony_ci                PA_TAG_SAMPLE_SPEC, &ss,
176953a5a1b3Sopenharmony_ci                PA_TAG_CHANNEL_MAP, &map,
177053a5a1b3Sopenharmony_ci                PA_TAG_U32, &sink_index,
177153a5a1b3Sopenharmony_ci                PA_TAG_STRING, &sink_name,
177253a5a1b3Sopenharmony_ci                PA_TAG_U32, &attr.maxlength,
177353a5a1b3Sopenharmony_ci                PA_TAG_BOOLEAN, &corked,
177453a5a1b3Sopenharmony_ci                PA_TAG_U32, &attr.tlength,
177553a5a1b3Sopenharmony_ci                PA_TAG_U32, &attr.prebuf,
177653a5a1b3Sopenharmony_ci                PA_TAG_U32, &attr.minreq,
177753a5a1b3Sopenharmony_ci                PA_TAG_U32, &syncid,
177853a5a1b3Sopenharmony_ci                PA_TAG_CVOLUME, &volume,
177953a5a1b3Sopenharmony_ci                PA_TAG_INVALID) < 0) {
178053a5a1b3Sopenharmony_ci
178153a5a1b3Sopenharmony_ci        protocol_error(c);
178253a5a1b3Sopenharmony_ci        goto finish;
178353a5a1b3Sopenharmony_ci    }
178453a5a1b3Sopenharmony_ci
178553a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
178653a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID, finish);
178753a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID, finish);
178853a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
178953a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
179053a5a1b3Sopenharmony_ci
179153a5a1b3Sopenharmony_ci    p = pa_proplist_new();
179253a5a1b3Sopenharmony_ci
179353a5a1b3Sopenharmony_ci    if (name)
179453a5a1b3Sopenharmony_ci        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
179553a5a1b3Sopenharmony_ci
179653a5a1b3Sopenharmony_ci    if (c->version >= 12) {
179753a5a1b3Sopenharmony_ci        /* Since 0.9.8 the user can ask for a couple of additional flags */
179853a5a1b3Sopenharmony_ci
179953a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
180053a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
180153a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
180253a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
180353a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
180453a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
180553a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
180653a5a1b3Sopenharmony_ci
180753a5a1b3Sopenharmony_ci            protocol_error(c);
180853a5a1b3Sopenharmony_ci            goto finish;
180953a5a1b3Sopenharmony_ci        }
181053a5a1b3Sopenharmony_ci    }
181153a5a1b3Sopenharmony_ci
181253a5a1b3Sopenharmony_ci    if (c->version >= 13) {
181353a5a1b3Sopenharmony_ci
181453a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &muted) < 0 ||
181553a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
181653a5a1b3Sopenharmony_ci            pa_tagstruct_get_proplist(t, p) < 0) {
181753a5a1b3Sopenharmony_ci
181853a5a1b3Sopenharmony_ci            protocol_error(c);
181953a5a1b3Sopenharmony_ci            goto finish;
182053a5a1b3Sopenharmony_ci        }
182153a5a1b3Sopenharmony_ci    }
182253a5a1b3Sopenharmony_ci
182353a5a1b3Sopenharmony_ci    if (c->version >= 14) {
182453a5a1b3Sopenharmony_ci
182553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
182653a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &early_requests) < 0) {
182753a5a1b3Sopenharmony_ci
182853a5a1b3Sopenharmony_ci            protocol_error(c);
182953a5a1b3Sopenharmony_ci            goto finish;
183053a5a1b3Sopenharmony_ci        }
183153a5a1b3Sopenharmony_ci    }
183253a5a1b3Sopenharmony_ci
183353a5a1b3Sopenharmony_ci    if (c->version >= 15) {
183453a5a1b3Sopenharmony_ci
183553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
183653a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
183753a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
183853a5a1b3Sopenharmony_ci
183953a5a1b3Sopenharmony_ci            protocol_error(c);
184053a5a1b3Sopenharmony_ci            goto finish;
184153a5a1b3Sopenharmony_ci        }
184253a5a1b3Sopenharmony_ci    }
184353a5a1b3Sopenharmony_ci
184453a5a1b3Sopenharmony_ci    if (c->version >= 17) {
184553a5a1b3Sopenharmony_ci
184653a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &relative_volume) < 0) {
184753a5a1b3Sopenharmony_ci
184853a5a1b3Sopenharmony_ci            protocol_error(c);
184953a5a1b3Sopenharmony_ci            goto finish;
185053a5a1b3Sopenharmony_ci        }
185153a5a1b3Sopenharmony_ci    }
185253a5a1b3Sopenharmony_ci
185353a5a1b3Sopenharmony_ci    if (c->version >= 18) {
185453a5a1b3Sopenharmony_ci
185553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &passthrough) < 0 ) {
185653a5a1b3Sopenharmony_ci            protocol_error(c);
185753a5a1b3Sopenharmony_ci            goto finish;
185853a5a1b3Sopenharmony_ci        }
185953a5a1b3Sopenharmony_ci    }
186053a5a1b3Sopenharmony_ci
186153a5a1b3Sopenharmony_ci    if (c->version >= 21) {
186253a5a1b3Sopenharmony_ci
186353a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu8(t, &n_formats) < 0) {
186453a5a1b3Sopenharmony_ci            protocol_error(c);
186553a5a1b3Sopenharmony_ci            goto finish;
186653a5a1b3Sopenharmony_ci        }
186753a5a1b3Sopenharmony_ci
186853a5a1b3Sopenharmony_ci        if (n_formats)
186953a5a1b3Sopenharmony_ci            formats = pa_idxset_new(NULL, NULL);
187053a5a1b3Sopenharmony_ci
187153a5a1b3Sopenharmony_ci        for (i = 0; i < n_formats; i++) {
187253a5a1b3Sopenharmony_ci            format = pa_format_info_new();
187353a5a1b3Sopenharmony_ci            if (pa_tagstruct_get_format_info(t, format) < 0) {
187453a5a1b3Sopenharmony_ci                protocol_error(c);
187553a5a1b3Sopenharmony_ci                goto finish;
187653a5a1b3Sopenharmony_ci            }
187753a5a1b3Sopenharmony_ci            pa_idxset_put(formats, format, NULL);
187853a5a1b3Sopenharmony_ci        }
187953a5a1b3Sopenharmony_ci    }
188053a5a1b3Sopenharmony_ci
188153a5a1b3Sopenharmony_ci    if (n_formats == 0) {
188253a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
188353a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels && volume.channels == ss.channels, tag, PA_ERR_INVALID, finish);
188453a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
188553a5a1b3Sopenharmony_ci    } else {
188653a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(format, formats, i) {
188753a5a1b3Sopenharmony_ci            CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
188853a5a1b3Sopenharmony_ci        }
188953a5a1b3Sopenharmony_ci    }
189053a5a1b3Sopenharmony_ci
189153a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
189253a5a1b3Sopenharmony_ci        protocol_error(c);
189353a5a1b3Sopenharmony_ci        goto finish;
189453a5a1b3Sopenharmony_ci    }
189553a5a1b3Sopenharmony_ci
189653a5a1b3Sopenharmony_ci    if (sink_index != PA_INVALID_INDEX) {
189753a5a1b3Sopenharmony_ci
189853a5a1b3Sopenharmony_ci        if (!(sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index))) {
189953a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
190053a5a1b3Sopenharmony_ci            goto finish;
190153a5a1b3Sopenharmony_ci        }
190253a5a1b3Sopenharmony_ci
190353a5a1b3Sopenharmony_ci    } else if (sink_name) {
190453a5a1b3Sopenharmony_ci
190553a5a1b3Sopenharmony_ci        if (!(sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK))) {
190653a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
190753a5a1b3Sopenharmony_ci            goto finish;
190853a5a1b3Sopenharmony_ci        }
190953a5a1b3Sopenharmony_ci    }
191053a5a1b3Sopenharmony_ci
191153a5a1b3Sopenharmony_ci    flags =
191253a5a1b3Sopenharmony_ci        (corked ? PA_SINK_INPUT_START_CORKED : 0) |
191353a5a1b3Sopenharmony_ci        (no_remap ? PA_SINK_INPUT_NO_REMAP : 0) |
191453a5a1b3Sopenharmony_ci        (no_remix ? PA_SINK_INPUT_NO_REMIX : 0) |
191553a5a1b3Sopenharmony_ci        (fix_format ? PA_SINK_INPUT_FIX_FORMAT : 0) |
191653a5a1b3Sopenharmony_ci        (fix_rate ? PA_SINK_INPUT_FIX_RATE : 0) |
191753a5a1b3Sopenharmony_ci        (fix_channels ? PA_SINK_INPUT_FIX_CHANNELS : 0) |
191853a5a1b3Sopenharmony_ci        (no_move ? PA_SINK_INPUT_DONT_MOVE : 0) |
191953a5a1b3Sopenharmony_ci        (variable_rate ? PA_SINK_INPUT_VARIABLE_RATE : 0) |
192053a5a1b3Sopenharmony_ci        (dont_inhibit_auto_suspend ? PA_SINK_INPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
192153a5a1b3Sopenharmony_ci        (fail_on_suspend ? PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND : 0) |
192253a5a1b3Sopenharmony_ci        (passthrough ? PA_SINK_INPUT_PASSTHROUGH : 0);
192353a5a1b3Sopenharmony_ci
192453a5a1b3Sopenharmony_ci    /* Only since protocol version 15 there's a separate muted_set
192553a5a1b3Sopenharmony_ci     * flag. For older versions we synthesize it here */
192653a5a1b3Sopenharmony_ci    muted_set = muted_set || muted;
192753a5a1b3Sopenharmony_ci
192853a5a1b3Sopenharmony_ci    s = playback_stream_new(c, sink, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, syncid, &missing, &ret);
192953a5a1b3Sopenharmony_ci    /* We no longer own the formats idxset */
193053a5a1b3Sopenharmony_ci    formats = NULL;
193153a5a1b3Sopenharmony_ci
193253a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
193353a5a1b3Sopenharmony_ci
193453a5a1b3Sopenharmony_ci    reply = reply_new(tag);
193553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, s->index);
193653a5a1b3Sopenharmony_ci    pa_assert(s->sink_input);
193753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, s->sink_input->index);
193853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, missing);
193953a5a1b3Sopenharmony_ci
194053a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
194153a5a1b3Sopenharmony_ci    pa_log("initial request is %u", missing);
194253a5a1b3Sopenharmony_ci#endif
194353a5a1b3Sopenharmony_ci
194453a5a1b3Sopenharmony_ci    if (c->version >= 9) {
194553a5a1b3Sopenharmony_ci        /* Since 0.9.0 we support sending the buffer metrics back to the client */
194653a5a1b3Sopenharmony_ci
194753a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
194853a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.tlength);
194953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.prebuf);
195053a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.minreq);
195153a5a1b3Sopenharmony_ci    }
195253a5a1b3Sopenharmony_ci
195353a5a1b3Sopenharmony_ci    if (c->version >= 12) {
195453a5a1b3Sopenharmony_ci        /* Since 0.9.8 we support sending the chosen sample
195553a5a1b3Sopenharmony_ci         * spec/channel map/device/suspend status back to the
195653a5a1b3Sopenharmony_ci         * client */
195753a5a1b3Sopenharmony_ci
195853a5a1b3Sopenharmony_ci        pa_tagstruct_put_sample_spec(reply, &ss);
195953a5a1b3Sopenharmony_ci        pa_tagstruct_put_channel_map(reply, &map);
196053a5a1b3Sopenharmony_ci
196153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->sink_input->sink->index);
196253a5a1b3Sopenharmony_ci        pa_tagstruct_puts(reply, s->sink_input->sink->name);
196353a5a1b3Sopenharmony_ci
196453a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, s->sink_input->sink->state == PA_SINK_SUSPENDED);
196553a5a1b3Sopenharmony_ci    }
196653a5a1b3Sopenharmony_ci
196753a5a1b3Sopenharmony_ci    if (c->version >= 13)
196853a5a1b3Sopenharmony_ci        pa_tagstruct_put_usec(reply, s->configured_sink_latency);
196953a5a1b3Sopenharmony_ci
197053a5a1b3Sopenharmony_ci    if (c->version >= 21) {
197153a5a1b3Sopenharmony_ci        /* Send back the format we negotiated */
197253a5a1b3Sopenharmony_ci        if (s->sink_input->format)
197353a5a1b3Sopenharmony_ci            pa_tagstruct_put_format_info(reply, s->sink_input->format);
197453a5a1b3Sopenharmony_ci        else {
197553a5a1b3Sopenharmony_ci            pa_format_info *f = pa_format_info_new();
197653a5a1b3Sopenharmony_ci            pa_tagstruct_put_format_info(reply, f);
197753a5a1b3Sopenharmony_ci            pa_format_info_free(f);
197853a5a1b3Sopenharmony_ci        }
197953a5a1b3Sopenharmony_ci    }
198053a5a1b3Sopenharmony_ci
198153a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
198253a5a1b3Sopenharmony_ci
198353a5a1b3Sopenharmony_cifinish:
198453a5a1b3Sopenharmony_ci    if (p)
198553a5a1b3Sopenharmony_ci        pa_proplist_free(p);
198653a5a1b3Sopenharmony_ci    if (formats)
198753a5a1b3Sopenharmony_ci        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
198853a5a1b3Sopenharmony_ci}
198953a5a1b3Sopenharmony_ci
199053a5a1b3Sopenharmony_cistatic void command_delete_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
199153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
199253a5a1b3Sopenharmony_ci    uint32_t channel;
199353a5a1b3Sopenharmony_ci
199453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
199553a5a1b3Sopenharmony_ci    pa_assert(t);
199653a5a1b3Sopenharmony_ci
199753a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &channel) < 0 ||
199853a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
199953a5a1b3Sopenharmony_ci        protocol_error(c);
200053a5a1b3Sopenharmony_ci        return;
200153a5a1b3Sopenharmony_ci    }
200253a5a1b3Sopenharmony_ci
200353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
200453a5a1b3Sopenharmony_ci
200553a5a1b3Sopenharmony_ci    switch (command) {
200653a5a1b3Sopenharmony_ci
200753a5a1b3Sopenharmony_ci        case PA_COMMAND_DELETE_PLAYBACK_STREAM: {
200853a5a1b3Sopenharmony_ci            playback_stream *s;
200953a5a1b3Sopenharmony_ci            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !playback_stream_isinstance(s)) {
201053a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
201153a5a1b3Sopenharmony_ci                return;
201253a5a1b3Sopenharmony_ci            }
201353a5a1b3Sopenharmony_ci
201453a5a1b3Sopenharmony_ci            playback_stream_unlink(s);
201553a5a1b3Sopenharmony_ci            break;
201653a5a1b3Sopenharmony_ci        }
201753a5a1b3Sopenharmony_ci
201853a5a1b3Sopenharmony_ci        case PA_COMMAND_DELETE_RECORD_STREAM: {
201953a5a1b3Sopenharmony_ci            record_stream *s;
202053a5a1b3Sopenharmony_ci            if (!(s = pa_idxset_get_by_index(c->record_streams, channel))) {
202153a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
202253a5a1b3Sopenharmony_ci                return;
202353a5a1b3Sopenharmony_ci            }
202453a5a1b3Sopenharmony_ci
202553a5a1b3Sopenharmony_ci            record_stream_unlink(s);
202653a5a1b3Sopenharmony_ci            break;
202753a5a1b3Sopenharmony_ci        }
202853a5a1b3Sopenharmony_ci
202953a5a1b3Sopenharmony_ci        case PA_COMMAND_DELETE_UPLOAD_STREAM: {
203053a5a1b3Sopenharmony_ci            upload_stream *s;
203153a5a1b3Sopenharmony_ci
203253a5a1b3Sopenharmony_ci            if (!(s = pa_idxset_get_by_index(c->output_streams, channel)) || !upload_stream_isinstance(s)) {
203353a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
203453a5a1b3Sopenharmony_ci                return;
203553a5a1b3Sopenharmony_ci            }
203653a5a1b3Sopenharmony_ci
203753a5a1b3Sopenharmony_ci            upload_stream_unlink(s);
203853a5a1b3Sopenharmony_ci            break;
203953a5a1b3Sopenharmony_ci        }
204053a5a1b3Sopenharmony_ci
204153a5a1b3Sopenharmony_ci        default:
204253a5a1b3Sopenharmony_ci            pa_assert_not_reached();
204353a5a1b3Sopenharmony_ci    }
204453a5a1b3Sopenharmony_ci
204553a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
204653a5a1b3Sopenharmony_ci}
204753a5a1b3Sopenharmony_ci
204853a5a1b3Sopenharmony_cistatic void command_create_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
204953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
205053a5a1b3Sopenharmony_ci    record_stream *s;
205153a5a1b3Sopenharmony_ci    pa_buffer_attr attr;
205253a5a1b3Sopenharmony_ci    uint32_t source_index;
205353a5a1b3Sopenharmony_ci    const char *name = NULL, *source_name;
205453a5a1b3Sopenharmony_ci    pa_sample_spec ss;
205553a5a1b3Sopenharmony_ci    pa_channel_map map;
205653a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
205753a5a1b3Sopenharmony_ci    pa_source *source = NULL;
205853a5a1b3Sopenharmony_ci    pa_cvolume volume;
205953a5a1b3Sopenharmony_ci    bool
206053a5a1b3Sopenharmony_ci        corked = false,
206153a5a1b3Sopenharmony_ci        no_remap = false,
206253a5a1b3Sopenharmony_ci        no_remix = false,
206353a5a1b3Sopenharmony_ci        fix_format = false,
206453a5a1b3Sopenharmony_ci        fix_rate = false,
206553a5a1b3Sopenharmony_ci        fix_channels = false,
206653a5a1b3Sopenharmony_ci        no_move = false,
206753a5a1b3Sopenharmony_ci        variable_rate = false,
206853a5a1b3Sopenharmony_ci        muted = false,
206953a5a1b3Sopenharmony_ci        adjust_latency = false,
207053a5a1b3Sopenharmony_ci        peak_detect = false,
207153a5a1b3Sopenharmony_ci        early_requests = false,
207253a5a1b3Sopenharmony_ci        dont_inhibit_auto_suspend = false,
207353a5a1b3Sopenharmony_ci        volume_set = false,
207453a5a1b3Sopenharmony_ci        muted_set = false,
207553a5a1b3Sopenharmony_ci        fail_on_suspend = false,
207653a5a1b3Sopenharmony_ci        relative_volume = false,
207753a5a1b3Sopenharmony_ci        passthrough = false;
207853a5a1b3Sopenharmony_ci
207953a5a1b3Sopenharmony_ci    pa_source_output_flags_t flags = 0;
208053a5a1b3Sopenharmony_ci    pa_proplist *p = NULL;
208153a5a1b3Sopenharmony_ci    uint32_t direct_on_input_idx = PA_INVALID_INDEX;
208253a5a1b3Sopenharmony_ci    pa_sink_input *direct_on_input = NULL;
208353a5a1b3Sopenharmony_ci    int ret = PA_ERR_INVALID;
208453a5a1b3Sopenharmony_ci    uint8_t n_formats = 0;
208553a5a1b3Sopenharmony_ci    pa_format_info *format;
208653a5a1b3Sopenharmony_ci    pa_idxset *formats = NULL;
208753a5a1b3Sopenharmony_ci    uint32_t i;
208853a5a1b3Sopenharmony_ci
208953a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
209053a5a1b3Sopenharmony_ci    pa_assert(t);
209153a5a1b3Sopenharmony_ci
209253a5a1b3Sopenharmony_ci    memset(&attr, 0, sizeof(attr));
209353a5a1b3Sopenharmony_ci
209453a5a1b3Sopenharmony_ci    if ((c->version < 13 && (pa_tagstruct_gets(t, &name) < 0 || !name)) ||
209553a5a1b3Sopenharmony_ci        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
209653a5a1b3Sopenharmony_ci        pa_tagstruct_get_channel_map(t, &map) < 0 ||
209753a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &source_index) < 0 ||
209853a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &source_name) < 0 ||
209953a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &attr.maxlength) < 0 ||
210053a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &corked) < 0 ||
210153a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &attr.fragsize) < 0) {
210253a5a1b3Sopenharmony_ci
210353a5a1b3Sopenharmony_ci        protocol_error(c);
210453a5a1b3Sopenharmony_ci        goto finish;
210553a5a1b3Sopenharmony_ci    }
210653a5a1b3Sopenharmony_ci
210753a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, c->authorized, tag, PA_ERR_ACCESS, finish);
210853a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, !source_name || pa_namereg_is_valid_name_or_wildcard(source_name, PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID, finish);
210953a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, source_index == PA_INVALID_INDEX || !source_name, tag, PA_ERR_INVALID, finish);
211053a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, !source_name || source_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID, finish);
211153a5a1b3Sopenharmony_ci
211253a5a1b3Sopenharmony_ci    p = pa_proplist_new();
211353a5a1b3Sopenharmony_ci
211453a5a1b3Sopenharmony_ci    if (name)
211553a5a1b3Sopenharmony_ci        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
211653a5a1b3Sopenharmony_ci
211753a5a1b3Sopenharmony_ci    if (c->version >= 12) {
211853a5a1b3Sopenharmony_ci        /* Since 0.9.8 the user can ask for a couple of additional flags */
211953a5a1b3Sopenharmony_ci
212053a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &no_remap) < 0 ||
212153a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &no_remix) < 0 ||
212253a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fix_format) < 0 ||
212353a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fix_rate) < 0 ||
212453a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fix_channels) < 0 ||
212553a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &no_move) < 0 ||
212653a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &variable_rate) < 0) {
212753a5a1b3Sopenharmony_ci
212853a5a1b3Sopenharmony_ci            protocol_error(c);
212953a5a1b3Sopenharmony_ci            goto finish;
213053a5a1b3Sopenharmony_ci        }
213153a5a1b3Sopenharmony_ci    }
213253a5a1b3Sopenharmony_ci
213353a5a1b3Sopenharmony_ci    if (c->version >= 13) {
213453a5a1b3Sopenharmony_ci
213553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &peak_detect) < 0 ||
213653a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &adjust_latency) < 0 ||
213753a5a1b3Sopenharmony_ci            pa_tagstruct_get_proplist(t, p) < 0 ||
213853a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &direct_on_input_idx) < 0) {
213953a5a1b3Sopenharmony_ci
214053a5a1b3Sopenharmony_ci            protocol_error(c);
214153a5a1b3Sopenharmony_ci            goto finish;
214253a5a1b3Sopenharmony_ci        }
214353a5a1b3Sopenharmony_ci    }
214453a5a1b3Sopenharmony_ci
214553a5a1b3Sopenharmony_ci    if (c->version >= 14) {
214653a5a1b3Sopenharmony_ci
214753a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &early_requests) < 0) {
214853a5a1b3Sopenharmony_ci            protocol_error(c);
214953a5a1b3Sopenharmony_ci            goto finish;
215053a5a1b3Sopenharmony_ci        }
215153a5a1b3Sopenharmony_ci    }
215253a5a1b3Sopenharmony_ci
215353a5a1b3Sopenharmony_ci    if (c->version >= 15) {
215453a5a1b3Sopenharmony_ci
215553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &dont_inhibit_auto_suspend) < 0 ||
215653a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &fail_on_suspend) < 0) {
215753a5a1b3Sopenharmony_ci
215853a5a1b3Sopenharmony_ci            protocol_error(c);
215953a5a1b3Sopenharmony_ci            goto finish;
216053a5a1b3Sopenharmony_ci        }
216153a5a1b3Sopenharmony_ci    }
216253a5a1b3Sopenharmony_ci
216353a5a1b3Sopenharmony_ci    if (c->version >= 22) {
216453a5a1b3Sopenharmony_ci        /* For newer client versions (with per-source-output volumes), we try
216553a5a1b3Sopenharmony_ci         * to make the behaviour for playback and record streams the same. */
216653a5a1b3Sopenharmony_ci        volume_set = true;
216753a5a1b3Sopenharmony_ci
216853a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu8(t, &n_formats) < 0) {
216953a5a1b3Sopenharmony_ci            protocol_error(c);
217053a5a1b3Sopenharmony_ci            goto finish;
217153a5a1b3Sopenharmony_ci        }
217253a5a1b3Sopenharmony_ci
217353a5a1b3Sopenharmony_ci        if (n_formats)
217453a5a1b3Sopenharmony_ci            formats = pa_idxset_new(NULL, NULL);
217553a5a1b3Sopenharmony_ci
217653a5a1b3Sopenharmony_ci        for (i = 0; i < n_formats; i++) {
217753a5a1b3Sopenharmony_ci            format = pa_format_info_new();
217853a5a1b3Sopenharmony_ci            if (pa_tagstruct_get_format_info(t, format) < 0) {
217953a5a1b3Sopenharmony_ci                protocol_error(c);
218053a5a1b3Sopenharmony_ci                goto finish;
218153a5a1b3Sopenharmony_ci            }
218253a5a1b3Sopenharmony_ci            pa_idxset_put(formats, format, NULL);
218353a5a1b3Sopenharmony_ci        }
218453a5a1b3Sopenharmony_ci
218553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_cvolume(t, &volume) < 0 ||
218653a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &muted) < 0 ||
218753a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &volume_set) < 0 ||
218853a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &muted_set) < 0 ||
218953a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &relative_volume) < 0 ||
219053a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &passthrough) < 0) {
219153a5a1b3Sopenharmony_ci
219253a5a1b3Sopenharmony_ci            protocol_error(c);
219353a5a1b3Sopenharmony_ci            goto finish;
219453a5a1b3Sopenharmony_ci        }
219553a5a1b3Sopenharmony_ci
219653a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID, finish);
219753a5a1b3Sopenharmony_ci    }
219853a5a1b3Sopenharmony_ci
219953a5a1b3Sopenharmony_ci    if (n_formats == 0) {
220053a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID, finish);
220153a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID, finish);
220253a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, c->version < 22 || (volume.channels == ss.channels), tag, PA_ERR_INVALID, finish);
220353a5a1b3Sopenharmony_ci        CHECK_VALIDITY_GOTO(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID, finish);
220453a5a1b3Sopenharmony_ci    } else {
220553a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(format, formats, i) {
220653a5a1b3Sopenharmony_ci            CHECK_VALIDITY_GOTO(c->pstream, pa_format_info_valid(format), tag, PA_ERR_INVALID, finish);
220753a5a1b3Sopenharmony_ci        }
220853a5a1b3Sopenharmony_ci    }
220953a5a1b3Sopenharmony_ci
221053a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
221153a5a1b3Sopenharmony_ci        protocol_error(c);
221253a5a1b3Sopenharmony_ci        goto finish;
221353a5a1b3Sopenharmony_ci    }
221453a5a1b3Sopenharmony_ci
221553a5a1b3Sopenharmony_ci    if (source_index != PA_INVALID_INDEX) {
221653a5a1b3Sopenharmony_ci
221753a5a1b3Sopenharmony_ci        if (!(source = pa_idxset_get_by_index(c->protocol->core->sources, source_index))) {
221853a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
221953a5a1b3Sopenharmony_ci            goto finish;
222053a5a1b3Sopenharmony_ci        }
222153a5a1b3Sopenharmony_ci
222253a5a1b3Sopenharmony_ci    } else if (source_name) {
222353a5a1b3Sopenharmony_ci
222453a5a1b3Sopenharmony_ci        if (!(source = pa_namereg_get(c->protocol->core, source_name, PA_NAMEREG_SOURCE))) {
222553a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
222653a5a1b3Sopenharmony_ci            goto finish;
222753a5a1b3Sopenharmony_ci        }
222853a5a1b3Sopenharmony_ci    }
222953a5a1b3Sopenharmony_ci
223053a5a1b3Sopenharmony_ci    if (direct_on_input_idx != PA_INVALID_INDEX) {
223153a5a1b3Sopenharmony_ci
223253a5a1b3Sopenharmony_ci        if (!(direct_on_input = pa_idxset_get_by_index(c->protocol->core->sink_inputs, direct_on_input_idx))) {
223353a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
223453a5a1b3Sopenharmony_ci            goto finish;
223553a5a1b3Sopenharmony_ci        }
223653a5a1b3Sopenharmony_ci    }
223753a5a1b3Sopenharmony_ci
223853a5a1b3Sopenharmony_ci    flags =
223953a5a1b3Sopenharmony_ci        (corked ? PA_SOURCE_OUTPUT_START_CORKED : 0) |
224053a5a1b3Sopenharmony_ci        (no_remap ? PA_SOURCE_OUTPUT_NO_REMAP : 0) |
224153a5a1b3Sopenharmony_ci        (no_remix ? PA_SOURCE_OUTPUT_NO_REMIX : 0) |
224253a5a1b3Sopenharmony_ci        (fix_format ? PA_SOURCE_OUTPUT_FIX_FORMAT : 0) |
224353a5a1b3Sopenharmony_ci        (fix_rate ? PA_SOURCE_OUTPUT_FIX_RATE : 0) |
224453a5a1b3Sopenharmony_ci        (fix_channels ? PA_SOURCE_OUTPUT_FIX_CHANNELS : 0) |
224553a5a1b3Sopenharmony_ci        (no_move ? PA_SOURCE_OUTPUT_DONT_MOVE : 0) |
224653a5a1b3Sopenharmony_ci        (variable_rate ? PA_SOURCE_OUTPUT_VARIABLE_RATE : 0) |
224753a5a1b3Sopenharmony_ci        (dont_inhibit_auto_suspend ? PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND : 0) |
224853a5a1b3Sopenharmony_ci        (fail_on_suspend ? PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND|PA_SOURCE_OUTPUT_KILL_ON_SUSPEND : 0) |
224953a5a1b3Sopenharmony_ci        (passthrough ? PA_SOURCE_OUTPUT_PASSTHROUGH : 0);
225053a5a1b3Sopenharmony_ci
225153a5a1b3Sopenharmony_ci    s = record_stream_new(c, source, &ss, &map, formats, &attr, volume_set ? &volume : NULL, muted, muted_set, flags, p, adjust_latency, early_requests, relative_volume, peak_detect, direct_on_input, &ret);
225253a5a1b3Sopenharmony_ci    /* We no longer own the formats idxset */
225353a5a1b3Sopenharmony_ci    formats = NULL;
225453a5a1b3Sopenharmony_ci
225553a5a1b3Sopenharmony_ci    CHECK_VALIDITY_GOTO(c->pstream, s, tag, ret, finish);
225653a5a1b3Sopenharmony_ci
225753a5a1b3Sopenharmony_ci    reply = reply_new(tag);
225853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, s->index);
225953a5a1b3Sopenharmony_ci    pa_assert(s->source_output);
226053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, s->source_output->index);
226153a5a1b3Sopenharmony_ci
226253a5a1b3Sopenharmony_ci    if (c->version >= 9) {
226353a5a1b3Sopenharmony_ci        /* Since 0.9 we support sending the buffer metrics back to the client */
226453a5a1b3Sopenharmony_ci
226553a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.maxlength);
226653a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, (uint32_t) s->buffer_attr.fragsize);
226753a5a1b3Sopenharmony_ci    }
226853a5a1b3Sopenharmony_ci
226953a5a1b3Sopenharmony_ci    if (c->version >= 12) {
227053a5a1b3Sopenharmony_ci        /* Since 0.9.8 we support sending the chosen sample
227153a5a1b3Sopenharmony_ci         * spec/channel map/device/suspend status back to the
227253a5a1b3Sopenharmony_ci         * client */
227353a5a1b3Sopenharmony_ci
227453a5a1b3Sopenharmony_ci        pa_tagstruct_put_sample_spec(reply, &ss);
227553a5a1b3Sopenharmony_ci        pa_tagstruct_put_channel_map(reply, &map);
227653a5a1b3Sopenharmony_ci
227753a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->source_output->source->index);
227853a5a1b3Sopenharmony_ci        pa_tagstruct_puts(reply, s->source_output->source->name);
227953a5a1b3Sopenharmony_ci
228053a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, s->source_output->source->state == PA_SOURCE_SUSPENDED);
228153a5a1b3Sopenharmony_ci    }
228253a5a1b3Sopenharmony_ci
228353a5a1b3Sopenharmony_ci    if (c->version >= 13)
228453a5a1b3Sopenharmony_ci        pa_tagstruct_put_usec(reply, s->configured_source_latency);
228553a5a1b3Sopenharmony_ci
228653a5a1b3Sopenharmony_ci    if (c->version >= 22) {
228753a5a1b3Sopenharmony_ci        /* Send back the format we negotiated */
228853a5a1b3Sopenharmony_ci        if (s->source_output->format)
228953a5a1b3Sopenharmony_ci            pa_tagstruct_put_format_info(reply, s->source_output->format);
229053a5a1b3Sopenharmony_ci        else {
229153a5a1b3Sopenharmony_ci            pa_format_info *f = pa_format_info_new();
229253a5a1b3Sopenharmony_ci            pa_tagstruct_put_format_info(reply, f);
229353a5a1b3Sopenharmony_ci            pa_format_info_free(f);
229453a5a1b3Sopenharmony_ci        }
229553a5a1b3Sopenharmony_ci    }
229653a5a1b3Sopenharmony_ci
229753a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
229853a5a1b3Sopenharmony_ci
229953a5a1b3Sopenharmony_cifinish:
230053a5a1b3Sopenharmony_ci    if (p)
230153a5a1b3Sopenharmony_ci        pa_proplist_free(p);
230253a5a1b3Sopenharmony_ci    if (formats)
230353a5a1b3Sopenharmony_ci        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
230453a5a1b3Sopenharmony_ci}
230553a5a1b3Sopenharmony_ci
230653a5a1b3Sopenharmony_cistatic void command_exit(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
230753a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
230853a5a1b3Sopenharmony_ci    int ret;
230953a5a1b3Sopenharmony_ci
231053a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
231153a5a1b3Sopenharmony_ci    pa_assert(t);
231253a5a1b3Sopenharmony_ci
231353a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
231453a5a1b3Sopenharmony_ci        protocol_error(c);
231553a5a1b3Sopenharmony_ci        return;
231653a5a1b3Sopenharmony_ci    }
231753a5a1b3Sopenharmony_ci
231853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
231953a5a1b3Sopenharmony_ci    ret = pa_core_exit(c->protocol->core, false, 0);
232053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, ret >= 0, tag, PA_ERR_ACCESS);
232153a5a1b3Sopenharmony_ci
232253a5a1b3Sopenharmony_ci    pa_log_debug("Client %s asks us to terminate.", pa_strnull(pa_proplist_gets(c->client->proplist,
232353a5a1b3Sopenharmony_ci        PA_PROP_APPLICATION_PROCESS_BINARY)));
232453a5a1b3Sopenharmony_ci
232553a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag); /* nonsense */
232653a5a1b3Sopenharmony_ci}
232753a5a1b3Sopenharmony_ci
232853a5a1b3Sopenharmony_cistatic void setup_srbchannel(pa_native_connection *c, pa_mem_type_t shm_type) {
232953a5a1b3Sopenharmony_ci    pa_srbchannel_template srbt;
233053a5a1b3Sopenharmony_ci    pa_srbchannel *srb;
233153a5a1b3Sopenharmony_ci    pa_memchunk mc;
233253a5a1b3Sopenharmony_ci    pa_tagstruct *t;
233353a5a1b3Sopenharmony_ci    int fdlist[2];
233453a5a1b3Sopenharmony_ci    pa_log_info("start setup_srbchannel, shm_type: %d", shm_type);
233553a5a1b3Sopenharmony_ci#ifndef HAVE_CREDS
233653a5a1b3Sopenharmony_ci    pa_log_debug("Disabling srbchannel, reason: No fd passing support");
233753a5a1b3Sopenharmony_ci    return;
233853a5a1b3Sopenharmony_ci#endif
233953a5a1b3Sopenharmony_ci
234053a5a1b3Sopenharmony_ci    if (!c->options->srbchannel) {
234153a5a1b3Sopenharmony_ci        pa_log_debug("Disabling srbchannel, reason: Must be enabled by module parameter");
234253a5a1b3Sopenharmony_ci        return;
234353a5a1b3Sopenharmony_ci    }
234453a5a1b3Sopenharmony_ci
234553a5a1b3Sopenharmony_ci    if (c->version < 30) {
234653a5a1b3Sopenharmony_ci        pa_log_debug("Disabling srbchannel, reason: Protocol too old");
234753a5a1b3Sopenharmony_ci        return;
234853a5a1b3Sopenharmony_ci    }
234953a5a1b3Sopenharmony_ci
235053a5a1b3Sopenharmony_ci    if (!pa_pstream_get_shm(c->pstream)) {
235153a5a1b3Sopenharmony_ci        pa_log_debug("Disabling srbchannel, reason: No SHM support");
235253a5a1b3Sopenharmony_ci        return;
235353a5a1b3Sopenharmony_ci    }
235453a5a1b3Sopenharmony_ci
235553a5a1b3Sopenharmony_ci    if (c->rw_mempool) {
235653a5a1b3Sopenharmony_ci        pa_log_debug("Ignoring srbchannel setup, reason: received COMMAND_AUTH "
235753a5a1b3Sopenharmony_ci                     "more than once");
235853a5a1b3Sopenharmony_ci        return;
235953a5a1b3Sopenharmony_ci    }
236053a5a1b3Sopenharmony_ci
236153a5a1b3Sopenharmony_ci    if (!(c->rw_mempool = pa_mempool_new(shm_type, c->protocol->core->shm_size, true))) {
236253a5a1b3Sopenharmony_ci        pa_log_debug("Disabling srbchannel, reason: Failed to allocate shared "
236353a5a1b3Sopenharmony_ci                    "writable memory pool.");
236453a5a1b3Sopenharmony_ci        return;
236553a5a1b3Sopenharmony_ci    }
236653a5a1b3Sopenharmony_ci
236753a5a1b3Sopenharmony_ci    if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
236853a5a1b3Sopenharmony_ci        const char *reason;
236953a5a1b3Sopenharmony_ci        if (pa_pstream_register_memfd_mempool(c->pstream, c->rw_mempool, &reason)) {
237053a5a1b3Sopenharmony_ci            pa_log_warn("Disabling srbchannel, reason: Failed to register memfd mempool: %s", reason);
237153a5a1b3Sopenharmony_ci            goto fail;
237253a5a1b3Sopenharmony_ci        }
237353a5a1b3Sopenharmony_ci    }
237453a5a1b3Sopenharmony_ci    pa_mempool_set_is_remote_writable(c->rw_mempool, true);
237553a5a1b3Sopenharmony_ci
237653a5a1b3Sopenharmony_ci    srb = pa_srbchannel_new(c->protocol->core->mainloop, c->rw_mempool);
237753a5a1b3Sopenharmony_ci    if (!srb) {
237853a5a1b3Sopenharmony_ci        pa_log_debug("Failed to create srbchannel");
237953a5a1b3Sopenharmony_ci        goto fail;
238053a5a1b3Sopenharmony_ci    }
238153a5a1b3Sopenharmony_ci    pa_log_debug("Enabling srbchannel...");
238253a5a1b3Sopenharmony_ci    pa_srbchannel_export(srb, &srbt);
238353a5a1b3Sopenharmony_ci
238453a5a1b3Sopenharmony_ci    /* Send enable command to client */
238553a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
238653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_ENABLE_SRBCHANNEL);
238753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (size_t) srb); /* tag */
238853a5a1b3Sopenharmony_ci    fdlist[0] = srbt.readfd;
238953a5a1b3Sopenharmony_ci    fdlist[1] = srbt.writefd;
239053a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct_with_fds(c->pstream, t, 2, fdlist, false);
239153a5a1b3Sopenharmony_ci
239253a5a1b3Sopenharmony_ci    /* Send ringbuffer memblock to client */
239353a5a1b3Sopenharmony_ci    mc.memblock = srbt.memblock;
239453a5a1b3Sopenharmony_ci    mc.index = 0;
239553a5a1b3Sopenharmony_ci    mc.length = pa_memblock_get_length(srbt.memblock);
239653a5a1b3Sopenharmony_ci    pa_pstream_send_memblock(c->pstream, 0, 0, 0, &mc);
239753a5a1b3Sopenharmony_ci
239853a5a1b3Sopenharmony_ci    c->srbpending = srb;
239953a5a1b3Sopenharmony_ci    return;
240053a5a1b3Sopenharmony_ci
240153a5a1b3Sopenharmony_cifail:
240253a5a1b3Sopenharmony_ci    if (c->rw_mempool) {
240353a5a1b3Sopenharmony_ci        pa_mempool_unref(c->rw_mempool);
240453a5a1b3Sopenharmony_ci        c->rw_mempool = NULL;
240553a5a1b3Sopenharmony_ci    }
240653a5a1b3Sopenharmony_ci}
240753a5a1b3Sopenharmony_ci
240853a5a1b3Sopenharmony_cistatic void command_enable_srbchannel(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
240953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
241053a5a1b3Sopenharmony_ci
241153a5a1b3Sopenharmony_ci    if (tag != (uint32_t) (size_t) c->srbpending) {
241253a5a1b3Sopenharmony_ci        protocol_error(c);
241353a5a1b3Sopenharmony_ci        return;
241453a5a1b3Sopenharmony_ci    }
241553a5a1b3Sopenharmony_ci
241653a5a1b3Sopenharmony_ci    pa_log_debug("Client enabled srbchannel.");
241753a5a1b3Sopenharmony_ci    pa_pstream_set_srbchannel(c->pstream, c->srbpending);
241853a5a1b3Sopenharmony_ci    c->srbpending = NULL;
241953a5a1b3Sopenharmony_ci}
242053a5a1b3Sopenharmony_ci
242153a5a1b3Sopenharmony_cistatic void command_auth(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
242253a5a1b3Sopenharmony_ci    pa_log_info("start command_authd");
242353a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
242453a5a1b3Sopenharmony_ci    const void*cookie;
242553a5a1b3Sopenharmony_ci    bool memfd_on_remote = false, do_memfd = false;
242653a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
242753a5a1b3Sopenharmony_ci    pa_mem_type_t shm_type;
242853a5a1b3Sopenharmony_ci    bool shm_on_remote = false, do_shm;
242953a5a1b3Sopenharmony_ci
243053a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
243153a5a1b3Sopenharmony_ci    pa_assert(t);
243253a5a1b3Sopenharmony_ci
243353a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &c->version) < 0 ||
243453a5a1b3Sopenharmony_ci        pa_tagstruct_get_arbitrary(t, &cookie, PA_NATIVE_COOKIE_LENGTH) < 0 ||
243553a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
243653a5a1b3Sopenharmony_ci        protocol_error(c);
243753a5a1b3Sopenharmony_ci        return;
243853a5a1b3Sopenharmony_ci    }
243953a5a1b3Sopenharmony_ci
244053a5a1b3Sopenharmony_ci    /* Minimum supported version */
244153a5a1b3Sopenharmony_ci    if (c->version < 8) {
244253a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_VERSION);
244353a5a1b3Sopenharmony_ci        return;
244453a5a1b3Sopenharmony_ci    }
244553a5a1b3Sopenharmony_ci
244653a5a1b3Sopenharmony_ci    /* Starting with protocol version 13 the MSB of the version tag
244753a5a1b3Sopenharmony_ci       reflects if shm is available for this pa_native_connection or
244853a5a1b3Sopenharmony_ci       not. */
244953a5a1b3Sopenharmony_ci    if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 13) {
245053a5a1b3Sopenharmony_ci        shm_on_remote = !!(c->version & PA_PROTOCOL_FLAG_SHM);
245153a5a1b3Sopenharmony_ci
245253a5a1b3Sopenharmony_ci        /* Starting with protocol version 31, the second MSB of the version
245353a5a1b3Sopenharmony_ci         * tag reflects whether memfd is supported on the other PA end. */
245453a5a1b3Sopenharmony_ci        if ((c->version & PA_PROTOCOL_VERSION_MASK) >= 31)
245553a5a1b3Sopenharmony_ci            memfd_on_remote = !!(c->version & PA_PROTOCOL_FLAG_MEMFD);
245653a5a1b3Sopenharmony_ci
245753a5a1b3Sopenharmony_ci        /* Reserve the two most-significant _bytes_ of the version tag
245853a5a1b3Sopenharmony_ci         * for flags. */
245953a5a1b3Sopenharmony_ci        c->version &= PA_PROTOCOL_VERSION_MASK;
246053a5a1b3Sopenharmony_ci    }
246153a5a1b3Sopenharmony_ci
246253a5a1b3Sopenharmony_ci    pa_log_debug("Protocol version: remote %u, local %u", c->version, PA_PROTOCOL_VERSION);
246353a5a1b3Sopenharmony_ci
246453a5a1b3Sopenharmony_ci    pa_proplist_setf(c->client->proplist, "native-protocol.version", "%u", c->version);
246553a5a1b3Sopenharmony_ci
246653a5a1b3Sopenharmony_ci    if (!c->authorized) {
246753a5a1b3Sopenharmony_ci        bool success = false;
246853a5a1b3Sopenharmony_ci
246953a5a1b3Sopenharmony_ci#ifdef HAVE_CREDS
247053a5a1b3Sopenharmony_ci        const pa_creds *creds;
247153a5a1b3Sopenharmony_ci
247253a5a1b3Sopenharmony_ci        if ((creds = pa_pdispatch_creds(pd))) {
247353a5a1b3Sopenharmony_ci            if (creds->uid == getuid())
247453a5a1b3Sopenharmony_ci                success = true;
247553a5a1b3Sopenharmony_ci            else if (c->options->auth_group) {
247653a5a1b3Sopenharmony_ci                int r;
247753a5a1b3Sopenharmony_ci                gid_t gid;
247853a5a1b3Sopenharmony_ci
247953a5a1b3Sopenharmony_ci                if ((gid = pa_get_gid_of_group(c->options->auth_group)) == (gid_t) -1)
248053a5a1b3Sopenharmony_ci                    pa_log_warn("Failed to get GID of group '%s'", c->options->auth_group);
248153a5a1b3Sopenharmony_ci                else if (gid == creds->gid)
248253a5a1b3Sopenharmony_ci                    success = true;
248353a5a1b3Sopenharmony_ci
248453a5a1b3Sopenharmony_ci                if (!success) {
248553a5a1b3Sopenharmony_ci                    if ((r = pa_uid_in_group(creds->uid, c->options->auth_group)) < 0)
248653a5a1b3Sopenharmony_ci                        pa_log_warn("Failed to check group membership.");
248753a5a1b3Sopenharmony_ci                    else if (r > 0)
248853a5a1b3Sopenharmony_ci                        success = true;
248953a5a1b3Sopenharmony_ci                }
249053a5a1b3Sopenharmony_ci            }
249153a5a1b3Sopenharmony_ci
249253a5a1b3Sopenharmony_ci            pa_log_debug("Got credentials: uid=%lu gid=%lu success=%i",
249353a5a1b3Sopenharmony_ci                        (unsigned long) creds->uid,
249453a5a1b3Sopenharmony_ci                        (unsigned long) creds->gid,
249553a5a1b3Sopenharmony_ci                        (int) success);
249653a5a1b3Sopenharmony_ci        }
249753a5a1b3Sopenharmony_ci#endif
249853a5a1b3Sopenharmony_ci
249953a5a1b3Sopenharmony_ci        if (!success && c->options->auth_cookie) {
250053a5a1b3Sopenharmony_ci            const uint8_t *ac;
250153a5a1b3Sopenharmony_ci
250253a5a1b3Sopenharmony_ci            if ((ac = pa_auth_cookie_read(c->options->auth_cookie, PA_NATIVE_COOKIE_LENGTH)))
250353a5a1b3Sopenharmony_ci                if (memcmp(ac, cookie, PA_NATIVE_COOKIE_LENGTH) == 0)
250453a5a1b3Sopenharmony_ci                    success = true;
250553a5a1b3Sopenharmony_ci
250653a5a1b3Sopenharmony_ci            /* All user process need to have the capability to connect and create pa stream,
250753a5a1b3Sopenharmony_ci             * so all of them can get the right cookie through ipc, cookie file check is useless.
250853a5a1b3Sopenharmony_ci             * We plan to use other way to protect some of the functions, instead of preventing
250953a5a1b3Sopenharmony_ci             * connection.
251053a5a1b3Sopenharmony_ci             */
251153a5a1b3Sopenharmony_ci            success = true;
251253a5a1b3Sopenharmony_ci        }
251353a5a1b3Sopenharmony_ci
251453a5a1b3Sopenharmony_ci        if (!success) {
251553a5a1b3Sopenharmony_ci            pa_log_warn("Denied access to client with invalid authentication data.");
251653a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_ACCESS);
251753a5a1b3Sopenharmony_ci            return;
251853a5a1b3Sopenharmony_ci        }
251953a5a1b3Sopenharmony_ci
252053a5a1b3Sopenharmony_ci        c->authorized = true;
252153a5a1b3Sopenharmony_ci        if (c->auth_timeout_event) {
252253a5a1b3Sopenharmony_ci            c->protocol->core->mainloop->time_free(c->auth_timeout_event);
252353a5a1b3Sopenharmony_ci            c->auth_timeout_event = NULL;
252453a5a1b3Sopenharmony_ci        }
252553a5a1b3Sopenharmony_ci    }
252653a5a1b3Sopenharmony_ci
252753a5a1b3Sopenharmony_ci    /* Enable shared memory and memfd support if possible */
252853a5a1b3Sopenharmony_ci    do_shm =
252953a5a1b3Sopenharmony_ci        pa_mempool_is_shared(c->protocol->core->mempool) &&
253053a5a1b3Sopenharmony_ci        c->is_local;
253153a5a1b3Sopenharmony_ci
253253a5a1b3Sopenharmony_ci    pa_log_debug("SHM possible: %s", pa_yes_no(do_shm));
253353a5a1b3Sopenharmony_ci
253453a5a1b3Sopenharmony_ci    if (do_shm)
253553a5a1b3Sopenharmony_ci        if (c->version < 10 || (c->version >= 13 && !shm_on_remote))
253653a5a1b3Sopenharmony_ci            do_shm = false;
253753a5a1b3Sopenharmony_ci
253853a5a1b3Sopenharmony_ci#ifdef HAVE_CREDS
253953a5a1b3Sopenharmony_ci    if (do_shm) {
254053a5a1b3Sopenharmony_ci        /* Only enable SHM if both sides are owned by the same
254153a5a1b3Sopenharmony_ci         * user. This is a security measure because otherwise data
254253a5a1b3Sopenharmony_ci         * private to the user might leak. */
254353a5a1b3Sopenharmony_ci
254453a5a1b3Sopenharmony_ci        const pa_creds *creds;
254553a5a1b3Sopenharmony_ci        if (!(creds = pa_pdispatch_creds(pd)) || getuid() != creds->uid)
254653a5a1b3Sopenharmony_ci            do_shm = false;
254753a5a1b3Sopenharmony_ci    }
254853a5a1b3Sopenharmony_ci#endif
254953a5a1b3Sopenharmony_ci
255053a5a1b3Sopenharmony_ci    pa_log_debug("Negotiated SHM: %s", pa_yes_no(do_shm));
255153a5a1b3Sopenharmony_ci    pa_pstream_enable_shm(c->pstream, do_shm);
255253a5a1b3Sopenharmony_ci
255353a5a1b3Sopenharmony_ci    /* Do not declare memfd support for 9.0 client libraries (protocol v31).
255453a5a1b3Sopenharmony_ci     *
255553a5a1b3Sopenharmony_ci     * Although they support memfd transport, such 9.0 clients has an iochannel
255653a5a1b3Sopenharmony_ci     * bug that would break memfd audio if they're run in 32-bit mode over a
255753a5a1b3Sopenharmony_ci     * 64-bit kernel. Thus influence them to use the POSIX shared memory model
255853a5a1b3Sopenharmony_ci     * instead. Check commit 451d1d676237c81 for further details. */
255953a5a1b3Sopenharmony_ci    do_memfd =
256053a5a1b3Sopenharmony_ci        c->version >= 32 && do_shm && pa_mempool_is_memfd_backed(c->protocol->core->mempool);
256153a5a1b3Sopenharmony_ci
256253a5a1b3Sopenharmony_ci    shm_type = PA_MEM_TYPE_PRIVATE;
256353a5a1b3Sopenharmony_ci    if (do_shm) {
256453a5a1b3Sopenharmony_ci        if (do_memfd && memfd_on_remote) {
256553a5a1b3Sopenharmony_ci            pa_pstream_enable_memfd(c->pstream);
256653a5a1b3Sopenharmony_ci            shm_type = PA_MEM_TYPE_SHARED_MEMFD;
256753a5a1b3Sopenharmony_ci        } else
256853a5a1b3Sopenharmony_ci            shm_type = PA_MEM_TYPE_SHARED_POSIX;
256953a5a1b3Sopenharmony_ci
257053a5a1b3Sopenharmony_ci        pa_log_info("Memfd possible: %s", pa_yes_no(pa_memfd_is_locally_supported()));
257153a5a1b3Sopenharmony_ci        pa_log_info("Negotiated SHM type: %s", pa_mem_type_to_string(shm_type));
257253a5a1b3Sopenharmony_ci    }
257353a5a1b3Sopenharmony_ci
257453a5a1b3Sopenharmony_ci    reply = reply_new(tag);
257553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_PROTOCOL_VERSION | (do_shm ? 0x80000000 : 0) |
257653a5a1b3Sopenharmony_ci                        (do_memfd ? 0x40000000 : 0));
257753a5a1b3Sopenharmony_ci
257853a5a1b3Sopenharmony_ci#ifdef HAVE_CREDS
257953a5a1b3Sopenharmony_ci{
258053a5a1b3Sopenharmony_ci    /* SHM support is only enabled after both sides made sure they are the same user. */
258153a5a1b3Sopenharmony_ci
258253a5a1b3Sopenharmony_ci    pa_creds ucred;
258353a5a1b3Sopenharmony_ci
258453a5a1b3Sopenharmony_ci    ucred.uid = getuid();
258553a5a1b3Sopenharmony_ci    ucred.gid = getgid();
258653a5a1b3Sopenharmony_ci
258753a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct_with_creds(c->pstream, reply, &ucred);
258853a5a1b3Sopenharmony_ci}
258953a5a1b3Sopenharmony_ci#else
259053a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
259153a5a1b3Sopenharmony_ci#endif
259253a5a1b3Sopenharmony_ci
259353a5a1b3Sopenharmony_ci    /* The client enables memfd transport on its pstream only after
259453a5a1b3Sopenharmony_ci     * inspecting our version flags to see if we support memfds too.
259553a5a1b3Sopenharmony_ci     *
259653a5a1b3Sopenharmony_ci     * Thus register any pools after sending the server's version
259753a5a1b3Sopenharmony_ci     * flags and _never_ before it. */
259853a5a1b3Sopenharmony_ci    if (shm_type == PA_MEM_TYPE_SHARED_MEMFD) {
259953a5a1b3Sopenharmony_ci        const char *reason;
260053a5a1b3Sopenharmony_ci
260153a5a1b3Sopenharmony_ci        if (pa_pstream_register_memfd_mempool(c->pstream, c->protocol->core->mempool, &reason))
260253a5a1b3Sopenharmony_ci            pa_log("Failed to register memfd mempool. Reason: %s", reason);
260353a5a1b3Sopenharmony_ci    }
260453a5a1b3Sopenharmony_ci
260553a5a1b3Sopenharmony_ci    setup_srbchannel(c, shm_type);
260653a5a1b3Sopenharmony_ci}
260753a5a1b3Sopenharmony_ci
260853a5a1b3Sopenharmony_cistatic void command_register_memfd_shmid(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
260953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
261053a5a1b3Sopenharmony_ci
261153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
261253a5a1b3Sopenharmony_ci    pa_assert(t);
261353a5a1b3Sopenharmony_ci
261453a5a1b3Sopenharmony_ci    if (pa_common_command_register_memfd_shmid(c->pstream, pd, c->version, command, t))
261553a5a1b3Sopenharmony_ci        protocol_error(c);
261653a5a1b3Sopenharmony_ci}
261753a5a1b3Sopenharmony_ci
261853a5a1b3Sopenharmony_cistatic void command_set_client_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
261953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
262053a5a1b3Sopenharmony_ci    const char *name = NULL;
262153a5a1b3Sopenharmony_ci    pa_proplist *p;
262253a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
262353a5a1b3Sopenharmony_ci
262453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
262553a5a1b3Sopenharmony_ci    pa_assert(t);
262653a5a1b3Sopenharmony_ci
262753a5a1b3Sopenharmony_ci    p = pa_proplist_new();
262853a5a1b3Sopenharmony_ci
262953a5a1b3Sopenharmony_ci    if ((c->version < 13 && pa_tagstruct_gets(t, &name) < 0) ||
263053a5a1b3Sopenharmony_ci        (c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
263153a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
263253a5a1b3Sopenharmony_ci
263353a5a1b3Sopenharmony_ci        protocol_error(c);
263453a5a1b3Sopenharmony_ci        pa_proplist_free(p);
263553a5a1b3Sopenharmony_ci        return;
263653a5a1b3Sopenharmony_ci    }
263753a5a1b3Sopenharmony_ci
263853a5a1b3Sopenharmony_ci    if (name)
263953a5a1b3Sopenharmony_ci        if (pa_proplist_sets(p, PA_PROP_APPLICATION_NAME, name) < 0) {
264053a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
264153a5a1b3Sopenharmony_ci            pa_proplist_free(p);
264253a5a1b3Sopenharmony_ci            return;
264353a5a1b3Sopenharmony_ci        }
264453a5a1b3Sopenharmony_ci
264553a5a1b3Sopenharmony_ci    pa_client_update_proplist(c->client, PA_UPDATE_REPLACE, p);
264653a5a1b3Sopenharmony_ci    pa_proplist_free(p);
264753a5a1b3Sopenharmony_ci
264853a5a1b3Sopenharmony_ci    reply = reply_new(tag);
264953a5a1b3Sopenharmony_ci
265053a5a1b3Sopenharmony_ci    if (c->version >= 13)
265153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, c->client->index);
265253a5a1b3Sopenharmony_ci
265353a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
265453a5a1b3Sopenharmony_ci}
265553a5a1b3Sopenharmony_ci
265653a5a1b3Sopenharmony_cistatic void command_lookup(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
265753a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
265853a5a1b3Sopenharmony_ci    const char *name;
265953a5a1b3Sopenharmony_ci    uint32_t idx = PA_IDXSET_INVALID;
266053a5a1b3Sopenharmony_ci
266153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
266253a5a1b3Sopenharmony_ci    pa_assert(t);
266353a5a1b3Sopenharmony_ci
266453a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &name) < 0 ||
266553a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
266653a5a1b3Sopenharmony_ci        protocol_error(c);
266753a5a1b3Sopenharmony_ci        return;
266853a5a1b3Sopenharmony_ci    }
266953a5a1b3Sopenharmony_ci
267053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
267153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_LOOKUP_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
267253a5a1b3Sopenharmony_ci
267353a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_LOOKUP_SINK) {
267453a5a1b3Sopenharmony_ci        pa_sink *sink;
267553a5a1b3Sopenharmony_ci        if ((sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK)))
267653a5a1b3Sopenharmony_ci            idx = sink->index;
267753a5a1b3Sopenharmony_ci    } else {
267853a5a1b3Sopenharmony_ci        pa_source *source;
267953a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_LOOKUP_SOURCE);
268053a5a1b3Sopenharmony_ci        if ((source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE)))
268153a5a1b3Sopenharmony_ci            idx = source->index;
268253a5a1b3Sopenharmony_ci    }
268353a5a1b3Sopenharmony_ci
268453a5a1b3Sopenharmony_ci    if (idx == PA_IDXSET_INVALID)
268553a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
268653a5a1b3Sopenharmony_ci    else {
268753a5a1b3Sopenharmony_ci        pa_tagstruct *reply;
268853a5a1b3Sopenharmony_ci        reply = reply_new(tag);
268953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, idx);
269053a5a1b3Sopenharmony_ci        pa_pstream_send_tagstruct(c->pstream, reply);
269153a5a1b3Sopenharmony_ci    }
269253a5a1b3Sopenharmony_ci}
269353a5a1b3Sopenharmony_ci
269453a5a1b3Sopenharmony_cistatic void command_drain_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
269553a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
269653a5a1b3Sopenharmony_ci    uint32_t idx;
269753a5a1b3Sopenharmony_ci    playback_stream *s;
269853a5a1b3Sopenharmony_ci
269953a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
270053a5a1b3Sopenharmony_ci    pa_assert(t);
270153a5a1b3Sopenharmony_ci
270253a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
270353a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
270453a5a1b3Sopenharmony_ci        protocol_error(c);
270553a5a1b3Sopenharmony_ci        return;
270653a5a1b3Sopenharmony_ci    }
270753a5a1b3Sopenharmony_ci
270853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
270953a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->output_streams, idx);
271053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
271153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
271253a5a1b3Sopenharmony_ci
271353a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_DRAIN, PA_UINT_TO_PTR(tag), 0, NULL, NULL);
271453a5a1b3Sopenharmony_ci}
271553a5a1b3Sopenharmony_ci
271653a5a1b3Sopenharmony_cistatic void command_stat(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
271753a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
271853a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
271953a5a1b3Sopenharmony_ci    const pa_mempool_stat *stat;
272053a5a1b3Sopenharmony_ci
272153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
272253a5a1b3Sopenharmony_ci    pa_assert(t);
272353a5a1b3Sopenharmony_ci
272453a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
272553a5a1b3Sopenharmony_ci        protocol_error(c);
272653a5a1b3Sopenharmony_ci        return;
272753a5a1b3Sopenharmony_ci    }
272853a5a1b3Sopenharmony_ci
272953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
273053a5a1b3Sopenharmony_ci
273153a5a1b3Sopenharmony_ci    stat = pa_mempool_get_stat(c->protocol->core->mempool);
273253a5a1b3Sopenharmony_ci
273353a5a1b3Sopenharmony_ci    reply = reply_new(tag);
273453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_allocated));
273553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->allocated_size));
273653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->n_accumulated));
273753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, (uint32_t) pa_atomic_load(&stat->accumulated_size));
273853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, (uint32_t) pa_scache_total_size(c->protocol->core));
273953a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
274053a5a1b3Sopenharmony_ci}
274153a5a1b3Sopenharmony_ci
274253a5a1b3Sopenharmony_cistatic void command_get_playback_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
274353a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
274453a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
274553a5a1b3Sopenharmony_ci    playback_stream *s;
274653a5a1b3Sopenharmony_ci    struct timeval tv, now;
274753a5a1b3Sopenharmony_ci    uint32_t idx;
274853a5a1b3Sopenharmony_ci
274953a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
275053a5a1b3Sopenharmony_ci    pa_assert(t);
275153a5a1b3Sopenharmony_ci
275253a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
275353a5a1b3Sopenharmony_ci        pa_tagstruct_get_timeval(t, &tv) < 0 ||
275453a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
275553a5a1b3Sopenharmony_ci        protocol_error(c);
275653a5a1b3Sopenharmony_ci        return;
275753a5a1b3Sopenharmony_ci    }
275853a5a1b3Sopenharmony_ci
275953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
276053a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->output_streams, idx);
276153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
276253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
276353a5a1b3Sopenharmony_ci
276453a5a1b3Sopenharmony_ci    /* Get an atomic snapshot of all timing parameters */
276553a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
276653a5a1b3Sopenharmony_ci
276753a5a1b3Sopenharmony_ci    reply = reply_new(tag);
276853a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(reply,
276953a5a1b3Sopenharmony_ci                          s->current_sink_latency +
277053a5a1b3Sopenharmony_ci                          pa_bytes_to_usec(s->render_memblockq_length, &s->sink_input->sink->sample_spec));
277153a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(reply, 0);
277253a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(reply,
277353a5a1b3Sopenharmony_ci                             s->playing_for > 0 &&
277453a5a1b3Sopenharmony_ci                             s->sink_input->sink->state == PA_SINK_RUNNING &&
277553a5a1b3Sopenharmony_ci                             s->sink_input->state == PA_SINK_INPUT_RUNNING);
277653a5a1b3Sopenharmony_ci    pa_tagstruct_put_timeval(reply, &tv);
277753a5a1b3Sopenharmony_ci    pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
277853a5a1b3Sopenharmony_ci    pa_tagstruct_puts64(reply, s->write_index);
277953a5a1b3Sopenharmony_ci    pa_tagstruct_puts64(reply, s->read_index);
278053a5a1b3Sopenharmony_ci
278153a5a1b3Sopenharmony_ci    if (c->version >= 13) {
278253a5a1b3Sopenharmony_ci        pa_tagstruct_putu64(reply, s->underrun_for);
278353a5a1b3Sopenharmony_ci        pa_tagstruct_putu64(reply, s->playing_for);
278453a5a1b3Sopenharmony_ci    }
278553a5a1b3Sopenharmony_ci
278653a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
278753a5a1b3Sopenharmony_ci}
278853a5a1b3Sopenharmony_ci
278953a5a1b3Sopenharmony_cistatic void command_get_record_latency(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
279053a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
279153a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
279253a5a1b3Sopenharmony_ci    record_stream *s;
279353a5a1b3Sopenharmony_ci    struct timeval tv, now;
279453a5a1b3Sopenharmony_ci    uint32_t idx;
279553a5a1b3Sopenharmony_ci
279653a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
279753a5a1b3Sopenharmony_ci    pa_assert(t);
279853a5a1b3Sopenharmony_ci
279953a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
280053a5a1b3Sopenharmony_ci        pa_tagstruct_get_timeval(t, &tv) < 0 ||
280153a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
280253a5a1b3Sopenharmony_ci        protocol_error(c);
280353a5a1b3Sopenharmony_ci        return;
280453a5a1b3Sopenharmony_ci    }
280553a5a1b3Sopenharmony_ci
280653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
280753a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->record_streams, idx);
280853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
280953a5a1b3Sopenharmony_ci
281053a5a1b3Sopenharmony_ci    /* Get an atomic snapshot of all timing parameters */
281153a5a1b3Sopenharmony_ci    pa_assert_se(pa_asyncmsgq_send(s->source_output->source->asyncmsgq, PA_MSGOBJECT(s->source_output), SOURCE_OUTPUT_MESSAGE_UPDATE_LATENCY, s, 0, NULL) == 0);
281253a5a1b3Sopenharmony_ci
281353a5a1b3Sopenharmony_ci    reply = reply_new(tag);
281453a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(reply, s->current_monitor_latency);
281553a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(reply,
281653a5a1b3Sopenharmony_ci                          s->current_source_latency +
281753a5a1b3Sopenharmony_ci                          pa_bytes_to_usec(s->on_the_fly_snapshot, &s->source_output->sample_spec));
281853a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(reply,
281953a5a1b3Sopenharmony_ci                             s->source_output->source->state == PA_SOURCE_RUNNING &&
282053a5a1b3Sopenharmony_ci                             s->source_output->state == PA_SOURCE_OUTPUT_RUNNING);
282153a5a1b3Sopenharmony_ci    pa_tagstruct_put_timeval(reply, &tv);
282253a5a1b3Sopenharmony_ci    pa_tagstruct_put_timeval(reply, pa_gettimeofday(&now));
282353a5a1b3Sopenharmony_ci    pa_tagstruct_puts64(reply, pa_memblockq_get_write_index(s->memblockq));
282453a5a1b3Sopenharmony_ci    pa_tagstruct_puts64(reply, pa_memblockq_get_read_index(s->memblockq));
282553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
282653a5a1b3Sopenharmony_ci}
282753a5a1b3Sopenharmony_ci
282853a5a1b3Sopenharmony_cistatic void command_create_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
282953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
283053a5a1b3Sopenharmony_ci    upload_stream *s;
283153a5a1b3Sopenharmony_ci    uint32_t length;
283253a5a1b3Sopenharmony_ci    const char *name = NULL;
283353a5a1b3Sopenharmony_ci    pa_sample_spec ss;
283453a5a1b3Sopenharmony_ci    pa_channel_map map;
283553a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
283653a5a1b3Sopenharmony_ci    pa_proplist *p;
283753a5a1b3Sopenharmony_ci
283853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
283953a5a1b3Sopenharmony_ci    pa_assert(t);
284053a5a1b3Sopenharmony_ci
284153a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &name) < 0 ||
284253a5a1b3Sopenharmony_ci        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
284353a5a1b3Sopenharmony_ci        pa_tagstruct_get_channel_map(t, &map) < 0 ||
284453a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &length) < 0) {
284553a5a1b3Sopenharmony_ci        protocol_error(c);
284653a5a1b3Sopenharmony_ci        return;
284753a5a1b3Sopenharmony_ci    }
284853a5a1b3Sopenharmony_ci
284953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
285053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, pa_sample_spec_valid(&ss), tag, PA_ERR_INVALID);
285153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, pa_channel_map_valid(&map), tag, PA_ERR_INVALID);
285253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, map.channels == ss.channels, tag, PA_ERR_INVALID);
285353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (length % pa_frame_size(&ss)) == 0 && length > 0, tag, PA_ERR_INVALID);
285453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, length <= PA_SCACHE_ENTRY_SIZE_MAX, tag, PA_ERR_TOOLARGE);
285553a5a1b3Sopenharmony_ci
285653a5a1b3Sopenharmony_ci    p = pa_proplist_new();
285753a5a1b3Sopenharmony_ci
285853a5a1b3Sopenharmony_ci    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
285953a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
286053a5a1b3Sopenharmony_ci
286153a5a1b3Sopenharmony_ci        protocol_error(c);
286253a5a1b3Sopenharmony_ci        pa_proplist_free(p);
286353a5a1b3Sopenharmony_ci        return;
286453a5a1b3Sopenharmony_ci    }
286553a5a1b3Sopenharmony_ci
286653a5a1b3Sopenharmony_ci    if (c->version < 13)
286753a5a1b3Sopenharmony_ci        pa_proplist_sets(p, PA_PROP_MEDIA_NAME, name);
286853a5a1b3Sopenharmony_ci    else if (!name)
286953a5a1b3Sopenharmony_ci        if (!(name = pa_proplist_gets(p, PA_PROP_EVENT_ID)))
287053a5a1b3Sopenharmony_ci            name = pa_proplist_gets(p, PA_PROP_MEDIA_NAME);
287153a5a1b3Sopenharmony_ci
287253a5a1b3Sopenharmony_ci    if (!name || !pa_namereg_is_valid_name(name)) {
287353a5a1b3Sopenharmony_ci        pa_proplist_free(p);
287453a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_INVALID);
287553a5a1b3Sopenharmony_ci    }
287653a5a1b3Sopenharmony_ci
287753a5a1b3Sopenharmony_ci    s = upload_stream_new(c, &ss, &map, name, length, p);
287853a5a1b3Sopenharmony_ci    pa_proplist_free(p);
287953a5a1b3Sopenharmony_ci
288053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_INVALID);
288153a5a1b3Sopenharmony_ci
288253a5a1b3Sopenharmony_ci    reply = reply_new(tag);
288353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, s->index);
288453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, length);
288553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
288653a5a1b3Sopenharmony_ci}
288753a5a1b3Sopenharmony_ci
288853a5a1b3Sopenharmony_cistatic void command_finish_upload_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
288953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
289053a5a1b3Sopenharmony_ci    uint32_t channel;
289153a5a1b3Sopenharmony_ci    upload_stream *s;
289253a5a1b3Sopenharmony_ci    uint32_t idx;
289353a5a1b3Sopenharmony_ci
289453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
289553a5a1b3Sopenharmony_ci    pa_assert(t);
289653a5a1b3Sopenharmony_ci
289753a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &channel) < 0 ||
289853a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
289953a5a1b3Sopenharmony_ci        protocol_error(c);
290053a5a1b3Sopenharmony_ci        return;
290153a5a1b3Sopenharmony_ci    }
290253a5a1b3Sopenharmony_ci
290353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
290453a5a1b3Sopenharmony_ci
290553a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->output_streams, channel);
290653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
290753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, upload_stream_isinstance(s), tag, PA_ERR_NOENTITY);
290853a5a1b3Sopenharmony_ci
290953a5a1b3Sopenharmony_ci    if (!s->memchunk.memblock)
291053a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_TOOLARGE);
291153a5a1b3Sopenharmony_ci    else if (pa_scache_add_item(c->protocol->core, s->name, &s->sample_spec, &s->channel_map, &s->memchunk, s->proplist, &idx) < 0)
291253a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_INTERNAL);
291353a5a1b3Sopenharmony_ci    else
291453a5a1b3Sopenharmony_ci        pa_pstream_send_simple_ack(c->pstream, tag);
291553a5a1b3Sopenharmony_ci
291653a5a1b3Sopenharmony_ci    upload_stream_unlink(s);
291753a5a1b3Sopenharmony_ci}
291853a5a1b3Sopenharmony_ci
291953a5a1b3Sopenharmony_cistatic void command_play_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
292053a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
292153a5a1b3Sopenharmony_ci    uint32_t sink_index;
292253a5a1b3Sopenharmony_ci    pa_volume_t volume;
292353a5a1b3Sopenharmony_ci    pa_sink *sink;
292453a5a1b3Sopenharmony_ci    const char *name, *sink_name;
292553a5a1b3Sopenharmony_ci    uint32_t idx;
292653a5a1b3Sopenharmony_ci    pa_proplist *p;
292753a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
292853a5a1b3Sopenharmony_ci
292953a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
293053a5a1b3Sopenharmony_ci    pa_assert(t);
293153a5a1b3Sopenharmony_ci
293253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
293353a5a1b3Sopenharmony_ci
293453a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &sink_index) < 0 ||
293553a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &sink_name) < 0 ||
293653a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &volume) < 0 ||
293753a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0) {
293853a5a1b3Sopenharmony_ci        protocol_error(c);
293953a5a1b3Sopenharmony_ci        return;
294053a5a1b3Sopenharmony_ci    }
294153a5a1b3Sopenharmony_ci
294253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !sink_name || pa_namereg_is_valid_name_or_wildcard(sink_name, PA_NAMEREG_SINK), tag, PA_ERR_INVALID);
294353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, sink_index == PA_INVALID_INDEX || !sink_name, tag, PA_ERR_INVALID);
294453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !sink_name || sink_index == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
294553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
294653a5a1b3Sopenharmony_ci
294753a5a1b3Sopenharmony_ci    if (sink_index != PA_INVALID_INDEX)
294853a5a1b3Sopenharmony_ci        sink = pa_idxset_get_by_index(c->protocol->core->sinks, sink_index);
294953a5a1b3Sopenharmony_ci    else
295053a5a1b3Sopenharmony_ci        sink = pa_namereg_get(c->protocol->core, sink_name, PA_NAMEREG_SINK);
295153a5a1b3Sopenharmony_ci
295253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
295353a5a1b3Sopenharmony_ci
295453a5a1b3Sopenharmony_ci    p = pa_proplist_new();
295553a5a1b3Sopenharmony_ci
295653a5a1b3Sopenharmony_ci    if ((c->version >= 13 && pa_tagstruct_get_proplist(t, p) < 0) ||
295753a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
295853a5a1b3Sopenharmony_ci        protocol_error(c);
295953a5a1b3Sopenharmony_ci        pa_proplist_free(p);
296053a5a1b3Sopenharmony_ci        return;
296153a5a1b3Sopenharmony_ci    }
296253a5a1b3Sopenharmony_ci
296353a5a1b3Sopenharmony_ci    pa_proplist_update(p, PA_UPDATE_MERGE, c->client->proplist);
296453a5a1b3Sopenharmony_ci
296553a5a1b3Sopenharmony_ci    if (pa_scache_play_item(c->protocol->core, name, sink, volume, p, &idx) < 0) {
296653a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
296753a5a1b3Sopenharmony_ci        pa_proplist_free(p);
296853a5a1b3Sopenharmony_ci        return;
296953a5a1b3Sopenharmony_ci    }
297053a5a1b3Sopenharmony_ci
297153a5a1b3Sopenharmony_ci    pa_proplist_free(p);
297253a5a1b3Sopenharmony_ci
297353a5a1b3Sopenharmony_ci    reply = reply_new(tag);
297453a5a1b3Sopenharmony_ci
297553a5a1b3Sopenharmony_ci    if (c->version >= 13)
297653a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, idx);
297753a5a1b3Sopenharmony_ci
297853a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
297953a5a1b3Sopenharmony_ci}
298053a5a1b3Sopenharmony_ci
298153a5a1b3Sopenharmony_cistatic void command_remove_sample(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
298253a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
298353a5a1b3Sopenharmony_ci    const char *name;
298453a5a1b3Sopenharmony_ci
298553a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
298653a5a1b3Sopenharmony_ci    pa_assert(t);
298753a5a1b3Sopenharmony_ci
298853a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &name) < 0 ||
298953a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
299053a5a1b3Sopenharmony_ci        protocol_error(c);
299153a5a1b3Sopenharmony_ci        return;
299253a5a1b3Sopenharmony_ci    }
299353a5a1b3Sopenharmony_ci
299453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
299553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, name && pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
299653a5a1b3Sopenharmony_ci
299753a5a1b3Sopenharmony_ci    if (pa_scache_remove_item(c->protocol->core, name) < 0) {
299853a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
299953a5a1b3Sopenharmony_ci        return;
300053a5a1b3Sopenharmony_ci    }
300153a5a1b3Sopenharmony_ci
300253a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
300353a5a1b3Sopenharmony_ci}
300453a5a1b3Sopenharmony_ci
300553a5a1b3Sopenharmony_cistatic void fixup_sample_spec(pa_native_connection *c, pa_sample_spec *fixed, const pa_sample_spec *original) {
300653a5a1b3Sopenharmony_ci    pa_assert(c);
300753a5a1b3Sopenharmony_ci    pa_assert(fixed);
300853a5a1b3Sopenharmony_ci    pa_assert(original);
300953a5a1b3Sopenharmony_ci
301053a5a1b3Sopenharmony_ci    *fixed = *original;
301153a5a1b3Sopenharmony_ci
301253a5a1b3Sopenharmony_ci    if (c->version < 12) {
301353a5a1b3Sopenharmony_ci        /* Before protocol version 12 we didn't support S32 samples,
301453a5a1b3Sopenharmony_ci         * so we need to lie about this to the client */
301553a5a1b3Sopenharmony_ci
301653a5a1b3Sopenharmony_ci        if (fixed->format == PA_SAMPLE_S32LE)
301753a5a1b3Sopenharmony_ci            fixed->format = PA_SAMPLE_FLOAT32LE;
301853a5a1b3Sopenharmony_ci        if (fixed->format == PA_SAMPLE_S32BE)
301953a5a1b3Sopenharmony_ci            fixed->format = PA_SAMPLE_FLOAT32BE;
302053a5a1b3Sopenharmony_ci    }
302153a5a1b3Sopenharmony_ci
302253a5a1b3Sopenharmony_ci    if (c->version < 15) {
302353a5a1b3Sopenharmony_ci        if (fixed->format == PA_SAMPLE_S24LE || fixed->format == PA_SAMPLE_S24_32LE)
302453a5a1b3Sopenharmony_ci            fixed->format = PA_SAMPLE_FLOAT32LE;
302553a5a1b3Sopenharmony_ci        if (fixed->format == PA_SAMPLE_S24BE || fixed->format == PA_SAMPLE_S24_32BE)
302653a5a1b3Sopenharmony_ci            fixed->format = PA_SAMPLE_FLOAT32BE;
302753a5a1b3Sopenharmony_ci    }
302853a5a1b3Sopenharmony_ci}
302953a5a1b3Sopenharmony_ci
303053a5a1b3Sopenharmony_cistatic void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink *sink) {
303153a5a1b3Sopenharmony_ci    pa_sample_spec fixed_ss;
303253a5a1b3Sopenharmony_ci
303353a5a1b3Sopenharmony_ci    pa_assert(t);
303453a5a1b3Sopenharmony_ci    pa_sink_assert_ref(sink);
303553a5a1b3Sopenharmony_ci
303653a5a1b3Sopenharmony_ci    fixup_sample_spec(c, &fixed_ss, &sink->sample_spec);
303753a5a1b3Sopenharmony_ci
303853a5a1b3Sopenharmony_ci    pa_tagstruct_put(
303953a5a1b3Sopenharmony_ci        t,
304053a5a1b3Sopenharmony_ci        PA_TAG_U32, sink->index,
304153a5a1b3Sopenharmony_ci        PA_TAG_STRING, sink->name,
304253a5a1b3Sopenharmony_ci        PA_TAG_STRING, pa_strnull(pa_proplist_gets(sink->proplist, PA_PROP_DEVICE_DESCRIPTION)),
304353a5a1b3Sopenharmony_ci        PA_TAG_SAMPLE_SPEC, &fixed_ss,
304453a5a1b3Sopenharmony_ci        PA_TAG_CHANNEL_MAP, &sink->channel_map,
304553a5a1b3Sopenharmony_ci        PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX,
304653a5a1b3Sopenharmony_ci        PA_TAG_CVOLUME, pa_sink_get_volume(sink, false),
304753a5a1b3Sopenharmony_ci        PA_TAG_BOOLEAN, pa_sink_get_mute(sink, false),
304853a5a1b3Sopenharmony_ci        PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,
304953a5a1b3Sopenharmony_ci        PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL,
305053a5a1b3Sopenharmony_ci        PA_TAG_USEC, pa_sink_get_latency(sink),
305153a5a1b3Sopenharmony_ci        PA_TAG_STRING, sink->driver,
305253a5a1b3Sopenharmony_ci        PA_TAG_U32, sink->flags & PA_SINK_CLIENT_FLAGS_MASK,
305353a5a1b3Sopenharmony_ci        PA_TAG_INVALID);
305453a5a1b3Sopenharmony_ci
305553a5a1b3Sopenharmony_ci    if (c->version >= 13) {
305653a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, sink->proplist);
305753a5a1b3Sopenharmony_ci        pa_tagstruct_put_usec(t, pa_sink_get_requested_latency(sink));
305853a5a1b3Sopenharmony_ci    }
305953a5a1b3Sopenharmony_ci
306053a5a1b3Sopenharmony_ci    if (c->version >= 15) {
306153a5a1b3Sopenharmony_ci        pa_tagstruct_put_volume(t, sink->base_volume);
306253a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(sink->state == PA_SINK_INVALID_STATE))
306353a5a1b3Sopenharmony_ci            pa_log_error("Internal sink state is invalid.");
306453a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, sink->state);
306553a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, sink->n_volume_steps);
306653a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
306753a5a1b3Sopenharmony_ci    }
306853a5a1b3Sopenharmony_ci
306953a5a1b3Sopenharmony_ci    if (c->version >= 16) {
307053a5a1b3Sopenharmony_ci        void *state;
307153a5a1b3Sopenharmony_ci        pa_device_port *p;
307253a5a1b3Sopenharmony_ci
307353a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, pa_hashmap_size(sink->ports));
307453a5a1b3Sopenharmony_ci
307553a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(p, sink->ports, state) {
307653a5a1b3Sopenharmony_ci            pa_tagstruct_puts(t, p->name);
307753a5a1b3Sopenharmony_ci            pa_tagstruct_puts(t, p->description);
307853a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, p->priority);
307953a5a1b3Sopenharmony_ci            if (c->version >= 24) {
308053a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, p->available);
308153a5a1b3Sopenharmony_ci                if (c->version >= 34) {
308253a5a1b3Sopenharmony_ci                    pa_tagstruct_puts(t, p->availability_group);
308353a5a1b3Sopenharmony_ci                    pa_tagstruct_putu32(t, p->type);
308453a5a1b3Sopenharmony_ci                }
308553a5a1b3Sopenharmony_ci            }
308653a5a1b3Sopenharmony_ci        }
308753a5a1b3Sopenharmony_ci
308853a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
308953a5a1b3Sopenharmony_ci    }
309053a5a1b3Sopenharmony_ci
309153a5a1b3Sopenharmony_ci    if (c->version >= 21) {
309253a5a1b3Sopenharmony_ci        uint32_t i;
309353a5a1b3Sopenharmony_ci        pa_format_info *f;
309453a5a1b3Sopenharmony_ci        pa_idxset *formats = pa_sink_get_formats(sink);
309553a5a1b3Sopenharmony_ci
309653a5a1b3Sopenharmony_ci        pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
309753a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(f, formats, i) {
309853a5a1b3Sopenharmony_ci            pa_tagstruct_put_format_info(t, f);
309953a5a1b3Sopenharmony_ci        }
310053a5a1b3Sopenharmony_ci
310153a5a1b3Sopenharmony_ci        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
310253a5a1b3Sopenharmony_ci    }
310353a5a1b3Sopenharmony_ci}
310453a5a1b3Sopenharmony_ci
310553a5a1b3Sopenharmony_cistatic void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
310653a5a1b3Sopenharmony_ci    pa_sample_spec fixed_ss;
310753a5a1b3Sopenharmony_ci
310853a5a1b3Sopenharmony_ci    pa_assert(t);
310953a5a1b3Sopenharmony_ci    pa_source_assert_ref(source);
311053a5a1b3Sopenharmony_ci
311153a5a1b3Sopenharmony_ci    fixup_sample_spec(c, &fixed_ss, &source->sample_spec);
311253a5a1b3Sopenharmony_ci
311353a5a1b3Sopenharmony_ci    pa_tagstruct_put(
311453a5a1b3Sopenharmony_ci        t,
311553a5a1b3Sopenharmony_ci        PA_TAG_U32, source->index,
311653a5a1b3Sopenharmony_ci        PA_TAG_STRING, source->name,
311753a5a1b3Sopenharmony_ci        PA_TAG_STRING, pa_strnull(pa_proplist_gets(source->proplist, PA_PROP_DEVICE_DESCRIPTION)),
311853a5a1b3Sopenharmony_ci        PA_TAG_SAMPLE_SPEC, &fixed_ss,
311953a5a1b3Sopenharmony_ci        PA_TAG_CHANNEL_MAP, &source->channel_map,
312053a5a1b3Sopenharmony_ci        PA_TAG_U32, source->module ? source->module->index : PA_INVALID_INDEX,
312153a5a1b3Sopenharmony_ci        PA_TAG_CVOLUME, pa_source_get_volume(source, false),
312253a5a1b3Sopenharmony_ci        PA_TAG_BOOLEAN, pa_source_get_mute(source, false),
312353a5a1b3Sopenharmony_ci        PA_TAG_U32, source->monitor_of ? source->monitor_of->index : PA_INVALID_INDEX,
312453a5a1b3Sopenharmony_ci        PA_TAG_STRING, source->monitor_of ? source->monitor_of->name : NULL,
312553a5a1b3Sopenharmony_ci        PA_TAG_USEC, pa_source_get_latency(source),
312653a5a1b3Sopenharmony_ci        PA_TAG_STRING, source->driver,
312753a5a1b3Sopenharmony_ci        PA_TAG_U32, source->flags & PA_SOURCE_CLIENT_FLAGS_MASK,
312853a5a1b3Sopenharmony_ci        PA_TAG_INVALID);
312953a5a1b3Sopenharmony_ci
313053a5a1b3Sopenharmony_ci    if (c->version >= 13) {
313153a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, source->proplist);
313253a5a1b3Sopenharmony_ci        pa_tagstruct_put_usec(t, pa_source_get_requested_latency(source));
313353a5a1b3Sopenharmony_ci    }
313453a5a1b3Sopenharmony_ci
313553a5a1b3Sopenharmony_ci    if (c->version >= 15) {
313653a5a1b3Sopenharmony_ci        pa_tagstruct_put_volume(t, source->base_volume);
313753a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(source->state == PA_SOURCE_INVALID_STATE))
313853a5a1b3Sopenharmony_ci            pa_log_error("Internal source state is invalid.");
313953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, source->state);
314053a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, source->n_volume_steps);
314153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
314253a5a1b3Sopenharmony_ci    }
314353a5a1b3Sopenharmony_ci
314453a5a1b3Sopenharmony_ci    if (c->version >= 16) {
314553a5a1b3Sopenharmony_ci        void *state;
314653a5a1b3Sopenharmony_ci        pa_device_port *p;
314753a5a1b3Sopenharmony_ci
314853a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, pa_hashmap_size(source->ports));
314953a5a1b3Sopenharmony_ci
315053a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(p, source->ports, state) {
315153a5a1b3Sopenharmony_ci            pa_tagstruct_puts(t, p->name);
315253a5a1b3Sopenharmony_ci            pa_tagstruct_puts(t, p->description);
315353a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, p->priority);
315453a5a1b3Sopenharmony_ci            if (c->version >= 24) {
315553a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, p->available);
315653a5a1b3Sopenharmony_ci                if (c->version >= 34) {
315753a5a1b3Sopenharmony_ci                    pa_tagstruct_puts(t, p->availability_group);
315853a5a1b3Sopenharmony_ci                    pa_tagstruct_putu32(t, p->type);
315953a5a1b3Sopenharmony_ci                }
316053a5a1b3Sopenharmony_ci            }
316153a5a1b3Sopenharmony_ci        }
316253a5a1b3Sopenharmony_ci
316353a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
316453a5a1b3Sopenharmony_ci    }
316553a5a1b3Sopenharmony_ci
316653a5a1b3Sopenharmony_ci    if (c->version >= 22) {
316753a5a1b3Sopenharmony_ci        uint32_t i;
316853a5a1b3Sopenharmony_ci        pa_format_info *f;
316953a5a1b3Sopenharmony_ci        pa_idxset *formats = pa_source_get_formats(source);
317053a5a1b3Sopenharmony_ci
317153a5a1b3Sopenharmony_ci        pa_tagstruct_putu8(t, (uint8_t) pa_idxset_size(formats));
317253a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(f, formats, i) {
317353a5a1b3Sopenharmony_ci            pa_tagstruct_put_format_info(t, f);
317453a5a1b3Sopenharmony_ci        }
317553a5a1b3Sopenharmony_ci
317653a5a1b3Sopenharmony_ci        pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
317753a5a1b3Sopenharmony_ci    }
317853a5a1b3Sopenharmony_ci}
317953a5a1b3Sopenharmony_ci
318053a5a1b3Sopenharmony_cistatic void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
318153a5a1b3Sopenharmony_ci    pa_assert(t);
318253a5a1b3Sopenharmony_ci    pa_assert(client);
318353a5a1b3Sopenharmony_ci
318453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, client->index);
318553a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(client->proplist, PA_PROP_APPLICATION_NAME)));
318653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, client->module ? client->module->index : PA_INVALID_INDEX);
318753a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, client->driver);
318853a5a1b3Sopenharmony_ci
318953a5a1b3Sopenharmony_ci    if (c->version >= 13)
319053a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, client->proplist);
319153a5a1b3Sopenharmony_ci}
319253a5a1b3Sopenharmony_ci
319353a5a1b3Sopenharmony_cistatic void card_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_card *card) {
319453a5a1b3Sopenharmony_ci    void *state = NULL;
319553a5a1b3Sopenharmony_ci    pa_card_profile *p;
319653a5a1b3Sopenharmony_ci    pa_device_port *port;
319753a5a1b3Sopenharmony_ci
319853a5a1b3Sopenharmony_ci    pa_assert(t);
319953a5a1b3Sopenharmony_ci    pa_assert(card);
320053a5a1b3Sopenharmony_ci
320153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, card->index);
320253a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, card->name);
320353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, card->module ? card->module->index : PA_INVALID_INDEX);
320453a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, card->driver);
320553a5a1b3Sopenharmony_ci
320653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, pa_hashmap_size(card->profiles));
320753a5a1b3Sopenharmony_ci
320853a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(p, card->profiles, state) {
320953a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, p->name);
321053a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, p->description);
321153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, p->n_sinks);
321253a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, p->n_sources);
321353a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, p->priority);
321453a5a1b3Sopenharmony_ci
321553a5a1b3Sopenharmony_ci        if (c->version >= 29)
321653a5a1b3Sopenharmony_ci            pa_tagstruct_putu32(t, (p->available != PA_AVAILABLE_NO));
321753a5a1b3Sopenharmony_ci    }
321853a5a1b3Sopenharmony_ci
321953a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, card->active_profile->name);
322053a5a1b3Sopenharmony_ci    pa_tagstruct_put_proplist(t, card->proplist);
322153a5a1b3Sopenharmony_ci
322253a5a1b3Sopenharmony_ci    if (c->version < 26)
322353a5a1b3Sopenharmony_ci        return;
322453a5a1b3Sopenharmony_ci
322553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, pa_hashmap_size(card->ports));
322653a5a1b3Sopenharmony_ci
322753a5a1b3Sopenharmony_ci    PA_HASHMAP_FOREACH(port, card->ports, state) {
322853a5a1b3Sopenharmony_ci        void *state2;
322953a5a1b3Sopenharmony_ci
323053a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, port->name);
323153a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, port->description);
323253a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, port->priority);
323353a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, port->available);
323453a5a1b3Sopenharmony_ci        pa_tagstruct_putu8(t, port->direction);
323553a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, port->proplist);
323653a5a1b3Sopenharmony_ci
323753a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, pa_hashmap_size(port->profiles));
323853a5a1b3Sopenharmony_ci
323953a5a1b3Sopenharmony_ci        PA_HASHMAP_FOREACH(p, port->profiles, state2)
324053a5a1b3Sopenharmony_ci            pa_tagstruct_puts(t, p->name);
324153a5a1b3Sopenharmony_ci
324253a5a1b3Sopenharmony_ci        if (c->version >= 27) {
324353a5a1b3Sopenharmony_ci            pa_tagstruct_puts64(t, port->latency_offset);
324453a5a1b3Sopenharmony_ci            if (c->version >= 34) {
324553a5a1b3Sopenharmony_ci                pa_tagstruct_puts(t, port->availability_group);
324653a5a1b3Sopenharmony_ci                pa_tagstruct_putu32(t, port->type);
324753a5a1b3Sopenharmony_ci            }
324853a5a1b3Sopenharmony_ci        }
324953a5a1b3Sopenharmony_ci    }
325053a5a1b3Sopenharmony_ci}
325153a5a1b3Sopenharmony_ci
325253a5a1b3Sopenharmony_cistatic void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) {
325353a5a1b3Sopenharmony_ci    pa_assert(t);
325453a5a1b3Sopenharmony_ci    pa_assert(module);
325553a5a1b3Sopenharmony_ci
325653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, module->index);
325753a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, module->name);
325853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, module->argument);
325953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module));
326053a5a1b3Sopenharmony_ci
326153a5a1b3Sopenharmony_ci    if (c->version < 15)
326253a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, false); /* autoload is obsolete */
326353a5a1b3Sopenharmony_ci
326453a5a1b3Sopenharmony_ci    if (c->version >= 15)
326553a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, module->proplist);
326653a5a1b3Sopenharmony_ci}
326753a5a1b3Sopenharmony_ci
326853a5a1b3Sopenharmony_cistatic void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) {
326953a5a1b3Sopenharmony_ci    pa_sample_spec fixed_ss;
327053a5a1b3Sopenharmony_ci    pa_usec_t sink_latency;
327153a5a1b3Sopenharmony_ci    pa_cvolume v;
327253a5a1b3Sopenharmony_ci    bool has_volume = false;
327353a5a1b3Sopenharmony_ci
327453a5a1b3Sopenharmony_ci    pa_assert(t);
327553a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(s);
327653a5a1b3Sopenharmony_ci
327753a5a1b3Sopenharmony_ci    fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
327853a5a1b3Sopenharmony_ci
327953a5a1b3Sopenharmony_ci    has_volume = pa_sink_input_is_volume_readable(s);
328053a5a1b3Sopenharmony_ci    if (has_volume)
328153a5a1b3Sopenharmony_ci        pa_sink_input_get_volume(s, &v, true);
328253a5a1b3Sopenharmony_ci    else
328353a5a1b3Sopenharmony_ci        pa_cvolume_reset(&v, fixed_ss.channels);
328453a5a1b3Sopenharmony_ci
328553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
328653a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
328753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
328853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
328953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->sink->index);
329053a5a1b3Sopenharmony_ci    pa_tagstruct_put_sample_spec(t, &fixed_ss);
329153a5a1b3Sopenharmony_ci    pa_tagstruct_put_channel_map(t, &s->channel_map);
329253a5a1b3Sopenharmony_ci    pa_tagstruct_put_cvolume(t, &v);
329353a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency));
329453a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(t, sink_latency);
329553a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s)));
329653a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, s->driver);
329753a5a1b3Sopenharmony_ci    if (c->version >= 11)
329853a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, s->muted);
329953a5a1b3Sopenharmony_ci    if (c->version >= 13)
330053a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, s->proplist);
330153a5a1b3Sopenharmony_ci    if (c->version >= 19)
330253a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, s->state == PA_SINK_INPUT_CORKED);
330353a5a1b3Sopenharmony_ci    if (c->version >= 20) {
330453a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, has_volume);
330553a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, s->volume_writable);
330653a5a1b3Sopenharmony_ci    }
330753a5a1b3Sopenharmony_ci    if (c->version >= 21)
330853a5a1b3Sopenharmony_ci        pa_tagstruct_put_format_info(t, s->format);
330953a5a1b3Sopenharmony_ci}
331053a5a1b3Sopenharmony_ci
331153a5a1b3Sopenharmony_cistatic void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) {
331253a5a1b3Sopenharmony_ci    pa_sample_spec fixed_ss;
331353a5a1b3Sopenharmony_ci    pa_usec_t source_latency;
331453a5a1b3Sopenharmony_ci    pa_cvolume v;
331553a5a1b3Sopenharmony_ci    bool has_volume = false;
331653a5a1b3Sopenharmony_ci
331753a5a1b3Sopenharmony_ci    pa_assert(t);
331853a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(s);
331953a5a1b3Sopenharmony_ci
332053a5a1b3Sopenharmony_ci    fixup_sample_spec(c, &fixed_ss, &s->sample_spec);
332153a5a1b3Sopenharmony_ci
332253a5a1b3Sopenharmony_ci    has_volume = pa_source_output_is_volume_readable(s);
332353a5a1b3Sopenharmony_ci    if (has_volume)
332453a5a1b3Sopenharmony_ci        pa_source_output_get_volume(s, &v, true);
332553a5a1b3Sopenharmony_ci    else
332653a5a1b3Sopenharmony_ci        pa_cvolume_reset(&v, fixed_ss.channels);
332753a5a1b3Sopenharmony_ci
332853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->index);
332953a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME)));
333053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX);
333153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->client ? s->client->index : PA_INVALID_INDEX);
333253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, s->source->index);
333353a5a1b3Sopenharmony_ci    pa_tagstruct_put_sample_spec(t, &fixed_ss);
333453a5a1b3Sopenharmony_ci    pa_tagstruct_put_channel_map(t, &s->channel_map);
333553a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(t, pa_source_output_get_latency(s, &source_latency));
333653a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(t, source_latency);
333753a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, pa_resample_method_to_string(pa_source_output_get_resample_method(s)));
333853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, s->driver);
333953a5a1b3Sopenharmony_ci    if (c->version >= 13)
334053a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, s->proplist);
334153a5a1b3Sopenharmony_ci    if (c->version >= 19)
334253a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, s->state == PA_SOURCE_OUTPUT_CORKED);
334353a5a1b3Sopenharmony_ci    if (c->version >= 22) {
334453a5a1b3Sopenharmony_ci        pa_tagstruct_put_cvolume(t, &v);
334553a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, s->muted);
334653a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, has_volume);
334753a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(t, s->volume_writable);
334853a5a1b3Sopenharmony_ci        pa_tagstruct_put_format_info(t, s->format);
334953a5a1b3Sopenharmony_ci    }
335053a5a1b3Sopenharmony_ci}
335153a5a1b3Sopenharmony_ci
335253a5a1b3Sopenharmony_cistatic void scache_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_scache_entry *e) {
335353a5a1b3Sopenharmony_ci    pa_sample_spec fixed_ss;
335453a5a1b3Sopenharmony_ci    pa_cvolume v;
335553a5a1b3Sopenharmony_ci
335653a5a1b3Sopenharmony_ci    pa_assert(t);
335753a5a1b3Sopenharmony_ci    pa_assert(e);
335853a5a1b3Sopenharmony_ci
335953a5a1b3Sopenharmony_ci    if (e->memchunk.memblock)
336053a5a1b3Sopenharmony_ci        fixup_sample_spec(c, &fixed_ss, &e->sample_spec);
336153a5a1b3Sopenharmony_ci    else
336253a5a1b3Sopenharmony_ci        memset(&fixed_ss, 0, sizeof(fixed_ss));
336353a5a1b3Sopenharmony_ci
336453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, e->index);
336553a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, e->name);
336653a5a1b3Sopenharmony_ci
336753a5a1b3Sopenharmony_ci    if (e->volume_is_set)
336853a5a1b3Sopenharmony_ci        v = e->volume;
336953a5a1b3Sopenharmony_ci    else
337053a5a1b3Sopenharmony_ci        pa_cvolume_init(&v);
337153a5a1b3Sopenharmony_ci
337253a5a1b3Sopenharmony_ci    pa_tagstruct_put_cvolume(t, &v);
337353a5a1b3Sopenharmony_ci    pa_tagstruct_put_usec(t, e->memchunk.memblock ? pa_bytes_to_usec(e->memchunk.length, &e->sample_spec) : 0);
337453a5a1b3Sopenharmony_ci    pa_tagstruct_put_sample_spec(t, &fixed_ss);
337553a5a1b3Sopenharmony_ci    pa_tagstruct_put_channel_map(t, &e->channel_map);
337653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) e->memchunk.length);
337753a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, e->lazy);
337853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, e->filename);
337953a5a1b3Sopenharmony_ci
338053a5a1b3Sopenharmony_ci    if (c->version >= 13)
338153a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(t, e->proplist);
338253a5a1b3Sopenharmony_ci}
338353a5a1b3Sopenharmony_ci
338453a5a1b3Sopenharmony_cistatic void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
338553a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
338653a5a1b3Sopenharmony_ci    uint32_t idx;
338753a5a1b3Sopenharmony_ci    pa_sink *sink = NULL;
338853a5a1b3Sopenharmony_ci    pa_source *source = NULL;
338953a5a1b3Sopenharmony_ci    pa_client *client = NULL;
339053a5a1b3Sopenharmony_ci    pa_card *card = NULL;
339153a5a1b3Sopenharmony_ci    pa_module *module = NULL;
339253a5a1b3Sopenharmony_ci    pa_sink_input *si = NULL;
339353a5a1b3Sopenharmony_ci    pa_source_output *so = NULL;
339453a5a1b3Sopenharmony_ci    pa_scache_entry *sce = NULL;
339553a5a1b3Sopenharmony_ci    const char *name = NULL;
339653a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
339753a5a1b3Sopenharmony_ci
339853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
339953a5a1b3Sopenharmony_ci    pa_assert(t);
340053a5a1b3Sopenharmony_ci
340153a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
340253a5a1b3Sopenharmony_ci        (command != PA_COMMAND_GET_CLIENT_INFO &&
340353a5a1b3Sopenharmony_ci         command != PA_COMMAND_GET_MODULE_INFO &&
340453a5a1b3Sopenharmony_ci         command != PA_COMMAND_GET_SINK_INPUT_INFO &&
340553a5a1b3Sopenharmony_ci         command != PA_COMMAND_GET_SOURCE_OUTPUT_INFO &&
340653a5a1b3Sopenharmony_ci         pa_tagstruct_gets(t, &name) < 0) ||
340753a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
340853a5a1b3Sopenharmony_ci        protocol_error(c);
340953a5a1b3Sopenharmony_ci        return;
341053a5a1b3Sopenharmony_ci    }
341153a5a1b3Sopenharmony_ci
341253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
341353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name ||
341453a5a1b3Sopenharmony_ci                   (command == PA_COMMAND_GET_SINK_INFO &&
341553a5a1b3Sopenharmony_ci                    pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SINK)) ||
341653a5a1b3Sopenharmony_ci                   (command == PA_COMMAND_GET_SOURCE_INFO &&
341753a5a1b3Sopenharmony_ci                    pa_namereg_is_valid_name_or_wildcard(name, PA_NAMEREG_SOURCE)) ||
341853a5a1b3Sopenharmony_ci                   pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
341953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, command == PA_COMMAND_GET_SINK_INFO ||
342053a5a1b3Sopenharmony_ci                   command == PA_COMMAND_GET_SOURCE_INFO ||
342153a5a1b3Sopenharmony_ci                   (idx != PA_INVALID_INDEX || name), tag, PA_ERR_INVALID);
342253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
342353a5a1b3Sopenharmony_ci
342453a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_GET_SINK_INFO) {
342553a5a1b3Sopenharmony_ci        if (idx != PA_INVALID_INDEX)
342653a5a1b3Sopenharmony_ci            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
342753a5a1b3Sopenharmony_ci        else
342853a5a1b3Sopenharmony_ci            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
342953a5a1b3Sopenharmony_ci    } else if (command == PA_COMMAND_GET_SOURCE_INFO) {
343053a5a1b3Sopenharmony_ci        if (idx != PA_INVALID_INDEX)
343153a5a1b3Sopenharmony_ci            source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
343253a5a1b3Sopenharmony_ci        else
343353a5a1b3Sopenharmony_ci            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
343453a5a1b3Sopenharmony_ci    } else if (command == PA_COMMAND_GET_CARD_INFO) {
343553a5a1b3Sopenharmony_ci        if (idx != PA_INVALID_INDEX)
343653a5a1b3Sopenharmony_ci            card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
343753a5a1b3Sopenharmony_ci        else
343853a5a1b3Sopenharmony_ci            card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
343953a5a1b3Sopenharmony_ci    } else if (command == PA_COMMAND_GET_CLIENT_INFO)
344053a5a1b3Sopenharmony_ci        client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
344153a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_MODULE_INFO)
344253a5a1b3Sopenharmony_ci        module = pa_idxset_get_by_index(c->protocol->core->modules, idx);
344353a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO)
344453a5a1b3Sopenharmony_ci        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
344553a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO)
344653a5a1b3Sopenharmony_ci        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
344753a5a1b3Sopenharmony_ci    else {
344853a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO);
344953a5a1b3Sopenharmony_ci        if (idx != PA_INVALID_INDEX)
345053a5a1b3Sopenharmony_ci            sce = pa_idxset_get_by_index(c->protocol->core->scache, idx);
345153a5a1b3Sopenharmony_ci        else
345253a5a1b3Sopenharmony_ci            sce = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SAMPLE);
345353a5a1b3Sopenharmony_ci    }
345453a5a1b3Sopenharmony_ci
345553a5a1b3Sopenharmony_ci    if (!sink && !source && !client && !card && !module && !si && !so && !sce) {
345653a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_NOENTITY);
345753a5a1b3Sopenharmony_ci        return;
345853a5a1b3Sopenharmony_ci    }
345953a5a1b3Sopenharmony_ci
346053a5a1b3Sopenharmony_ci    reply = reply_new(tag);
346153a5a1b3Sopenharmony_ci    if (sink)
346253a5a1b3Sopenharmony_ci        sink_fill_tagstruct(c, reply, sink);
346353a5a1b3Sopenharmony_ci    else if (source)
346453a5a1b3Sopenharmony_ci        source_fill_tagstruct(c, reply, source);
346553a5a1b3Sopenharmony_ci    else if (client)
346653a5a1b3Sopenharmony_ci        client_fill_tagstruct(c, reply, client);
346753a5a1b3Sopenharmony_ci    else if (card)
346853a5a1b3Sopenharmony_ci        card_fill_tagstruct(c, reply, card);
346953a5a1b3Sopenharmony_ci    else if (module)
347053a5a1b3Sopenharmony_ci        module_fill_tagstruct(c, reply, module);
347153a5a1b3Sopenharmony_ci    else if (si)
347253a5a1b3Sopenharmony_ci        sink_input_fill_tagstruct(c, reply, si);
347353a5a1b3Sopenharmony_ci    else if (so)
347453a5a1b3Sopenharmony_ci        source_output_fill_tagstruct(c, reply, so);
347553a5a1b3Sopenharmony_ci    else
347653a5a1b3Sopenharmony_ci        scache_fill_tagstruct(c, reply, sce);
347753a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
347853a5a1b3Sopenharmony_ci}
347953a5a1b3Sopenharmony_ci
348053a5a1b3Sopenharmony_cistatic void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
348153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
348253a5a1b3Sopenharmony_ci    pa_idxset *i;
348353a5a1b3Sopenharmony_ci    uint32_t idx;
348453a5a1b3Sopenharmony_ci    void *p;
348553a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
348653a5a1b3Sopenharmony_ci
348753a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
348853a5a1b3Sopenharmony_ci    pa_assert(t);
348953a5a1b3Sopenharmony_ci
349053a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
349153a5a1b3Sopenharmony_ci        protocol_error(c);
349253a5a1b3Sopenharmony_ci        return;
349353a5a1b3Sopenharmony_ci    }
349453a5a1b3Sopenharmony_ci
349553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
349653a5a1b3Sopenharmony_ci
349753a5a1b3Sopenharmony_ci    reply = reply_new(tag);
349853a5a1b3Sopenharmony_ci
349953a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_GET_SINK_INFO_LIST)
350053a5a1b3Sopenharmony_ci        i = c->protocol->core->sinks;
350153a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
350253a5a1b3Sopenharmony_ci        i = c->protocol->core->sources;
350353a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
350453a5a1b3Sopenharmony_ci        i = c->protocol->core->clients;
350553a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
350653a5a1b3Sopenharmony_ci        i = c->protocol->core->cards;
350753a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
350853a5a1b3Sopenharmony_ci        i = c->protocol->core->modules;
350953a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
351053a5a1b3Sopenharmony_ci        i = c->protocol->core->sink_inputs;
351153a5a1b3Sopenharmony_ci    else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
351253a5a1b3Sopenharmony_ci        i = c->protocol->core->source_outputs;
351353a5a1b3Sopenharmony_ci    else {
351453a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
351553a5a1b3Sopenharmony_ci        i = c->protocol->core->scache;
351653a5a1b3Sopenharmony_ci    }
351753a5a1b3Sopenharmony_ci
351853a5a1b3Sopenharmony_ci    if (i) {
351953a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(p, i, idx) {
352053a5a1b3Sopenharmony_ci            if (command == PA_COMMAND_GET_SINK_INFO_LIST)
352153a5a1b3Sopenharmony_ci                sink_fill_tagstruct(c, reply, p);
352253a5a1b3Sopenharmony_ci            else if (command == PA_COMMAND_GET_SOURCE_INFO_LIST)
352353a5a1b3Sopenharmony_ci                source_fill_tagstruct(c, reply, p);
352453a5a1b3Sopenharmony_ci            else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST)
352553a5a1b3Sopenharmony_ci                client_fill_tagstruct(c, reply, p);
352653a5a1b3Sopenharmony_ci            else if (command == PA_COMMAND_GET_CARD_INFO_LIST)
352753a5a1b3Sopenharmony_ci                card_fill_tagstruct(c, reply, p);
352853a5a1b3Sopenharmony_ci            else if (command == PA_COMMAND_GET_MODULE_INFO_LIST)
352953a5a1b3Sopenharmony_ci                module_fill_tagstruct(c, reply, p);
353053a5a1b3Sopenharmony_ci            else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST)
353153a5a1b3Sopenharmony_ci                sink_input_fill_tagstruct(c, reply, p);
353253a5a1b3Sopenharmony_ci            else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST)
353353a5a1b3Sopenharmony_ci                source_output_fill_tagstruct(c, reply, p);
353453a5a1b3Sopenharmony_ci            else {
353553a5a1b3Sopenharmony_ci                pa_assert(command == PA_COMMAND_GET_SAMPLE_INFO_LIST);
353653a5a1b3Sopenharmony_ci                scache_fill_tagstruct(c, reply, p);
353753a5a1b3Sopenharmony_ci            }
353853a5a1b3Sopenharmony_ci        }
353953a5a1b3Sopenharmony_ci    }
354053a5a1b3Sopenharmony_ci
354153a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
354253a5a1b3Sopenharmony_ci}
354353a5a1b3Sopenharmony_ci
354453a5a1b3Sopenharmony_cistatic void command_get_server_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
354553a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
354653a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
354753a5a1b3Sopenharmony_ci    pa_sample_spec fixed_ss;
354853a5a1b3Sopenharmony_ci    char *h, *u;
354953a5a1b3Sopenharmony_ci    pa_core *core;
355053a5a1b3Sopenharmony_ci
355153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
355253a5a1b3Sopenharmony_ci    pa_assert(t);
355353a5a1b3Sopenharmony_ci
355453a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
355553a5a1b3Sopenharmony_ci        protocol_error(c);
355653a5a1b3Sopenharmony_ci        return;
355753a5a1b3Sopenharmony_ci    }
355853a5a1b3Sopenharmony_ci
355953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
356053a5a1b3Sopenharmony_ci
356153a5a1b3Sopenharmony_ci    reply = reply_new(tag);
356253a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, PACKAGE_NAME);
356353a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, PACKAGE_VERSION);
356453a5a1b3Sopenharmony_ci
356553a5a1b3Sopenharmony_ci    u = pa_get_user_name_malloc();
356653a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, u);
356753a5a1b3Sopenharmony_ci    pa_xfree(u);
356853a5a1b3Sopenharmony_ci
356953a5a1b3Sopenharmony_ci    h = pa_get_host_name_malloc();
357053a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, h);
357153a5a1b3Sopenharmony_ci    pa_xfree(h);
357253a5a1b3Sopenharmony_ci
357353a5a1b3Sopenharmony_ci    core = c->protocol->core;
357453a5a1b3Sopenharmony_ci
357553a5a1b3Sopenharmony_ci    fixup_sample_spec(c, &fixed_ss, &core->default_sample_spec);
357653a5a1b3Sopenharmony_ci    pa_tagstruct_put_sample_spec(reply, &fixed_ss);
357753a5a1b3Sopenharmony_ci
357853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, core->default_sink ? core->default_sink->name : NULL);
357953a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, core->default_source ? core->default_source->name : NULL);
358053a5a1b3Sopenharmony_ci
358153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, c->protocol->core->cookie);
358253a5a1b3Sopenharmony_ci
358353a5a1b3Sopenharmony_ci    if (c->version >= 15)
358453a5a1b3Sopenharmony_ci        pa_tagstruct_put_channel_map(reply, &core->default_channel_map);
358553a5a1b3Sopenharmony_ci
358653a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
358753a5a1b3Sopenharmony_ci}
358853a5a1b3Sopenharmony_ci
358953a5a1b3Sopenharmony_cistatic void subscription_cb(pa_core *core, pa_subscription_event_type_t e, uint32_t idx, void *userdata) {
359053a5a1b3Sopenharmony_ci    pa_tagstruct *t;
359153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
359253a5a1b3Sopenharmony_ci
359353a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
359453a5a1b3Sopenharmony_ci
359553a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
359653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE_EVENT);
359753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1);
359853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, e);
359953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, idx);
360053a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, t);
360153a5a1b3Sopenharmony_ci}
360253a5a1b3Sopenharmony_ci
360353a5a1b3Sopenharmony_cistatic void command_subscribe(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
360453a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
360553a5a1b3Sopenharmony_ci    pa_subscription_mask_t m;
360653a5a1b3Sopenharmony_ci
360753a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
360853a5a1b3Sopenharmony_ci    pa_assert(t);
360953a5a1b3Sopenharmony_ci
361053a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &m) < 0 ||
361153a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
361253a5a1b3Sopenharmony_ci        protocol_error(c);
361353a5a1b3Sopenharmony_ci        return;
361453a5a1b3Sopenharmony_ci    }
361553a5a1b3Sopenharmony_ci
361653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
361753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (m & ~PA_SUBSCRIPTION_MASK_ALL) == 0, tag, PA_ERR_INVALID);
361853a5a1b3Sopenharmony_ci
361953a5a1b3Sopenharmony_ci    if (c->subscription)
362053a5a1b3Sopenharmony_ci        pa_subscription_free(c->subscription);
362153a5a1b3Sopenharmony_ci
362253a5a1b3Sopenharmony_ci    if (m != 0) {
362353a5a1b3Sopenharmony_ci        c->subscription = pa_subscription_new(c->protocol->core, m, subscription_cb, c);
362453a5a1b3Sopenharmony_ci        pa_assert(c->subscription);
362553a5a1b3Sopenharmony_ci    } else
362653a5a1b3Sopenharmony_ci        c->subscription = NULL;
362753a5a1b3Sopenharmony_ci
362853a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
362953a5a1b3Sopenharmony_ci}
363053a5a1b3Sopenharmony_ci
363153a5a1b3Sopenharmony_cistatic void command_set_volume(
363253a5a1b3Sopenharmony_ci        pa_pdispatch *pd,
363353a5a1b3Sopenharmony_ci        uint32_t command,
363453a5a1b3Sopenharmony_ci        uint32_t tag,
363553a5a1b3Sopenharmony_ci        pa_tagstruct *t,
363653a5a1b3Sopenharmony_ci        void *userdata) {
363753a5a1b3Sopenharmony_ci
363853a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
363953a5a1b3Sopenharmony_ci    uint32_t idx;
364053a5a1b3Sopenharmony_ci    pa_cvolume volume;
364153a5a1b3Sopenharmony_ci    pa_sink *sink = NULL;
364253a5a1b3Sopenharmony_ci    pa_source *source = NULL;
364353a5a1b3Sopenharmony_ci    pa_sink_input *si = NULL;
364453a5a1b3Sopenharmony_ci    pa_source_output *so = NULL;
364553a5a1b3Sopenharmony_ci    const char *name = NULL;
364653a5a1b3Sopenharmony_ci    const char *client_name;
364753a5a1b3Sopenharmony_ci
364853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
364953a5a1b3Sopenharmony_ci    pa_assert(t);
365053a5a1b3Sopenharmony_ci
365153a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
365253a5a1b3Sopenharmony_ci        (command == PA_COMMAND_SET_SINK_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
365353a5a1b3Sopenharmony_ci        (command == PA_COMMAND_SET_SOURCE_VOLUME && pa_tagstruct_gets(t, &name) < 0) ||
365453a5a1b3Sopenharmony_ci        pa_tagstruct_get_cvolume(t, &volume) ||
365553a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
365653a5a1b3Sopenharmony_ci        protocol_error(c);
365753a5a1b3Sopenharmony_ci        return;
365853a5a1b3Sopenharmony_ci    }
365953a5a1b3Sopenharmony_ci
366053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
366153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_VOLUME ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
366253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
366353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, pa_cvolume_valid(&volume), tag, PA_ERR_INVALID);
366453a5a1b3Sopenharmony_ci
366553a5a1b3Sopenharmony_ci    switch (command) {
366653a5a1b3Sopenharmony_ci
366753a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SINK_VOLUME:
366853a5a1b3Sopenharmony_ci            if (idx != PA_INVALID_INDEX)
366953a5a1b3Sopenharmony_ci                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
367053a5a1b3Sopenharmony_ci            else
367153a5a1b3Sopenharmony_ci                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
367253a5a1b3Sopenharmony_ci            break;
367353a5a1b3Sopenharmony_ci
367453a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SOURCE_VOLUME:
367553a5a1b3Sopenharmony_ci            if (idx != PA_INVALID_INDEX)
367653a5a1b3Sopenharmony_ci                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
367753a5a1b3Sopenharmony_ci            else
367853a5a1b3Sopenharmony_ci                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
367953a5a1b3Sopenharmony_ci            break;
368053a5a1b3Sopenharmony_ci
368153a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SINK_INPUT_VOLUME:
368253a5a1b3Sopenharmony_ci            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
368353a5a1b3Sopenharmony_ci            break;
368453a5a1b3Sopenharmony_ci
368553a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME:
368653a5a1b3Sopenharmony_ci            so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
368753a5a1b3Sopenharmony_ci            break;
368853a5a1b3Sopenharmony_ci
368953a5a1b3Sopenharmony_ci        default:
369053a5a1b3Sopenharmony_ci            pa_assert_not_reached();
369153a5a1b3Sopenharmony_ci    }
369253a5a1b3Sopenharmony_ci
369353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
369453a5a1b3Sopenharmony_ci
369553a5a1b3Sopenharmony_ci    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
369653a5a1b3Sopenharmony_ci
369753a5a1b3Sopenharmony_ci    if (sink) {
369853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &sink->sample_spec), tag, PA_ERR_INVALID);
369953a5a1b3Sopenharmony_ci
370053a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name);
370153a5a1b3Sopenharmony_ci        pa_sink_set_volume(sink, &volume, true, true);
370253a5a1b3Sopenharmony_ci    } else if (source) {
370353a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &source->sample_spec), tag, PA_ERR_INVALID);
370453a5a1b3Sopenharmony_ci
370553a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes volume of source %s.", client_name, source->name);
370653a5a1b3Sopenharmony_ci        pa_source_set_volume(source, &volume, true, true);
370753a5a1b3Sopenharmony_ci    } else if (si) {
370853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, si->volume_writable, tag, PA_ERR_BADSTATE);
370953a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID);
371053a5a1b3Sopenharmony_ci
371153a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes volume of sink input %s.",
371253a5a1b3Sopenharmony_ci                     client_name,
371353a5a1b3Sopenharmony_ci                     pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
371453a5a1b3Sopenharmony_ci        pa_sink_input_set_volume(si, &volume, true, true);
371553a5a1b3Sopenharmony_ci    } else if (so) {
371653a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, so->volume_writable, tag, PA_ERR_BADSTATE);
371753a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &so->sample_spec), tag, PA_ERR_INVALID);
371853a5a1b3Sopenharmony_ci
371953a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes volume of source output %s.",
372053a5a1b3Sopenharmony_ci                     client_name,
372153a5a1b3Sopenharmony_ci                     pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
372253a5a1b3Sopenharmony_ci        pa_source_output_set_volume(so, &volume, true, true);
372353a5a1b3Sopenharmony_ci    }
372453a5a1b3Sopenharmony_ci
372553a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
372653a5a1b3Sopenharmony_ci}
372753a5a1b3Sopenharmony_ci
372853a5a1b3Sopenharmony_cistatic void command_set_mute(
372953a5a1b3Sopenharmony_ci        pa_pdispatch *pd,
373053a5a1b3Sopenharmony_ci        uint32_t command,
373153a5a1b3Sopenharmony_ci        uint32_t tag,
373253a5a1b3Sopenharmony_ci        pa_tagstruct *t,
373353a5a1b3Sopenharmony_ci        void *userdata) {
373453a5a1b3Sopenharmony_ci
373553a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
373653a5a1b3Sopenharmony_ci    uint32_t idx;
373753a5a1b3Sopenharmony_ci    bool mute;
373853a5a1b3Sopenharmony_ci    pa_sink *sink = NULL;
373953a5a1b3Sopenharmony_ci    pa_source *source = NULL;
374053a5a1b3Sopenharmony_ci    pa_sink_input *si = NULL;
374153a5a1b3Sopenharmony_ci    pa_source_output *so = NULL;
374253a5a1b3Sopenharmony_ci    const char *name = NULL, *client_name;
374353a5a1b3Sopenharmony_ci
374453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
374553a5a1b3Sopenharmony_ci    pa_assert(t);
374653a5a1b3Sopenharmony_ci
374753a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
374853a5a1b3Sopenharmony_ci        (command == PA_COMMAND_SET_SINK_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
374953a5a1b3Sopenharmony_ci        (command == PA_COMMAND_SET_SOURCE_MUTE && pa_tagstruct_gets(t, &name) < 0) ||
375053a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &mute) ||
375153a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
375253a5a1b3Sopenharmony_ci        protocol_error(c);
375353a5a1b3Sopenharmony_ci        return;
375453a5a1b3Sopenharmony_ci    }
375553a5a1b3Sopenharmony_ci
375653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
375753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_MUTE ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
375853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
375953a5a1b3Sopenharmony_ci
376053a5a1b3Sopenharmony_ci    switch (command) {
376153a5a1b3Sopenharmony_ci
376253a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SINK_MUTE:
376353a5a1b3Sopenharmony_ci            if (idx != PA_INVALID_INDEX)
376453a5a1b3Sopenharmony_ci                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
376553a5a1b3Sopenharmony_ci            else
376653a5a1b3Sopenharmony_ci                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
376753a5a1b3Sopenharmony_ci
376853a5a1b3Sopenharmony_ci            break;
376953a5a1b3Sopenharmony_ci
377053a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SOURCE_MUTE:
377153a5a1b3Sopenharmony_ci            if (idx != PA_INVALID_INDEX)
377253a5a1b3Sopenharmony_ci                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
377353a5a1b3Sopenharmony_ci            else
377453a5a1b3Sopenharmony_ci                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
377553a5a1b3Sopenharmony_ci
377653a5a1b3Sopenharmony_ci            break;
377753a5a1b3Sopenharmony_ci
377853a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SINK_INPUT_MUTE:
377953a5a1b3Sopenharmony_ci            si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
378053a5a1b3Sopenharmony_ci            break;
378153a5a1b3Sopenharmony_ci
378253a5a1b3Sopenharmony_ci        case PA_COMMAND_SET_SOURCE_OUTPUT_MUTE:
378353a5a1b3Sopenharmony_ci            so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
378453a5a1b3Sopenharmony_ci            break;
378553a5a1b3Sopenharmony_ci
378653a5a1b3Sopenharmony_ci        default:
378753a5a1b3Sopenharmony_ci            pa_assert_not_reached();
378853a5a1b3Sopenharmony_ci    }
378953a5a1b3Sopenharmony_ci
379053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, si || so || sink || source, tag, PA_ERR_NOENTITY);
379153a5a1b3Sopenharmony_ci
379253a5a1b3Sopenharmony_ci    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
379353a5a1b3Sopenharmony_ci
379453a5a1b3Sopenharmony_ci    if (sink) {
379553a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes mute of sink %s.", client_name, sink->name);
379653a5a1b3Sopenharmony_ci        pa_sink_set_mute(sink, mute, true);
379753a5a1b3Sopenharmony_ci    } else if (source) {
379853a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes mute of source %s.", client_name, source->name);
379953a5a1b3Sopenharmony_ci        pa_source_set_mute(source, mute, true);
380053a5a1b3Sopenharmony_ci    } else if (si) {
380153a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes mute of sink input %s.",
380253a5a1b3Sopenharmony_ci                     client_name,
380353a5a1b3Sopenharmony_ci                     pa_strnull(pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)));
380453a5a1b3Sopenharmony_ci        pa_sink_input_set_mute(si, mute, true);
380553a5a1b3Sopenharmony_ci    } else if (so) {
380653a5a1b3Sopenharmony_ci        pa_log_debug("Client %s changes mute of source output %s.",
380753a5a1b3Sopenharmony_ci                     client_name,
380853a5a1b3Sopenharmony_ci                     pa_strnull(pa_proplist_gets(so->proplist, PA_PROP_MEDIA_NAME)));
380953a5a1b3Sopenharmony_ci        pa_source_output_set_mute(so, mute, true);
381053a5a1b3Sopenharmony_ci    }
381153a5a1b3Sopenharmony_ci
381253a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
381353a5a1b3Sopenharmony_ci}
381453a5a1b3Sopenharmony_ci
381553a5a1b3Sopenharmony_cistatic void command_cork_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
381653a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
381753a5a1b3Sopenharmony_ci    uint32_t idx;
381853a5a1b3Sopenharmony_ci    bool b;
381953a5a1b3Sopenharmony_ci    playback_stream *s;
382053a5a1b3Sopenharmony_ci
382153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
382253a5a1b3Sopenharmony_ci    pa_assert(t);
382353a5a1b3Sopenharmony_ci
382453a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
382553a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &b) < 0 ||
382653a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
382753a5a1b3Sopenharmony_ci        protocol_error(c);
382853a5a1b3Sopenharmony_ci        return;
382953a5a1b3Sopenharmony_ci    }
383053a5a1b3Sopenharmony_ci
383153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
383253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
383353a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->output_streams, idx);
383453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
383553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
383653a5a1b3Sopenharmony_ci
383753a5a1b3Sopenharmony_ci    pa_sink_input_cork(s->sink_input, b);
383853a5a1b3Sopenharmony_ci
383953a5a1b3Sopenharmony_ci    if (b)
384053a5a1b3Sopenharmony_ci        s->is_underrun = true;
384153a5a1b3Sopenharmony_ci
384253a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
384353a5a1b3Sopenharmony_ci}
384453a5a1b3Sopenharmony_ci
384553a5a1b3Sopenharmony_cistatic void command_trigger_or_flush_or_prebuf_playback_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
384653a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
384753a5a1b3Sopenharmony_ci    uint32_t idx;
384853a5a1b3Sopenharmony_ci    playback_stream *s;
384953a5a1b3Sopenharmony_ci
385053a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
385153a5a1b3Sopenharmony_ci    pa_assert(t);
385253a5a1b3Sopenharmony_ci
385353a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
385453a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
385553a5a1b3Sopenharmony_ci        protocol_error(c);
385653a5a1b3Sopenharmony_ci        return;
385753a5a1b3Sopenharmony_ci    }
385853a5a1b3Sopenharmony_ci
385953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
386053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
386153a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->output_streams, idx);
386253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
386353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
386453a5a1b3Sopenharmony_ci
386553a5a1b3Sopenharmony_ci    switch (command) {
386653a5a1b3Sopenharmony_ci        case PA_COMMAND_FLUSH_PLAYBACK_STREAM:
386753a5a1b3Sopenharmony_ci            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_FLUSH, NULL, 0, NULL);
386853a5a1b3Sopenharmony_ci            break;
386953a5a1b3Sopenharmony_ci
387053a5a1b3Sopenharmony_ci        case PA_COMMAND_PREBUF_PLAYBACK_STREAM:
387153a5a1b3Sopenharmony_ci            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_PREBUF_FORCE, NULL, 0, NULL);
387253a5a1b3Sopenharmony_ci            break;
387353a5a1b3Sopenharmony_ci
387453a5a1b3Sopenharmony_ci        case PA_COMMAND_TRIGGER_PLAYBACK_STREAM:
387553a5a1b3Sopenharmony_ci            pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_TRIGGER, NULL, 0, NULL);
387653a5a1b3Sopenharmony_ci            break;
387753a5a1b3Sopenharmony_ci
387853a5a1b3Sopenharmony_ci        default:
387953a5a1b3Sopenharmony_ci            pa_assert_not_reached();
388053a5a1b3Sopenharmony_ci    }
388153a5a1b3Sopenharmony_ci
388253a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
388353a5a1b3Sopenharmony_ci}
388453a5a1b3Sopenharmony_ci
388553a5a1b3Sopenharmony_cistatic void command_cork_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
388653a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
388753a5a1b3Sopenharmony_ci    uint32_t idx;
388853a5a1b3Sopenharmony_ci    record_stream *s;
388953a5a1b3Sopenharmony_ci    bool b;
389053a5a1b3Sopenharmony_ci
389153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
389253a5a1b3Sopenharmony_ci    pa_assert(t);
389353a5a1b3Sopenharmony_ci
389453a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
389553a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &b) < 0 ||
389653a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
389753a5a1b3Sopenharmony_ci        protocol_error(c);
389853a5a1b3Sopenharmony_ci        return;
389953a5a1b3Sopenharmony_ci    }
390053a5a1b3Sopenharmony_ci
390153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
390253a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->record_streams, idx);
390353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
390453a5a1b3Sopenharmony_ci
390553a5a1b3Sopenharmony_ci    pa_source_output_cork(s->source_output, b);
390653a5a1b3Sopenharmony_ci    pa_memblockq_prebuf_force(s->memblockq);
390753a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
390853a5a1b3Sopenharmony_ci}
390953a5a1b3Sopenharmony_ci
391053a5a1b3Sopenharmony_cistatic void command_flush_record_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
391153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
391253a5a1b3Sopenharmony_ci    uint32_t idx;
391353a5a1b3Sopenharmony_ci    record_stream *s;
391453a5a1b3Sopenharmony_ci
391553a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
391653a5a1b3Sopenharmony_ci    pa_assert(t);
391753a5a1b3Sopenharmony_ci
391853a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
391953a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
392053a5a1b3Sopenharmony_ci        protocol_error(c);
392153a5a1b3Sopenharmony_ci        return;
392253a5a1b3Sopenharmony_ci    }
392353a5a1b3Sopenharmony_ci
392453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
392553a5a1b3Sopenharmony_ci    s = pa_idxset_get_by_index(c->record_streams, idx);
392653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
392753a5a1b3Sopenharmony_ci
392853a5a1b3Sopenharmony_ci    pa_memblockq_flush_read(s->memblockq);
392953a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
393053a5a1b3Sopenharmony_ci}
393153a5a1b3Sopenharmony_ci
393253a5a1b3Sopenharmony_cistatic void command_set_stream_buffer_attr(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
393353a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
393453a5a1b3Sopenharmony_ci    uint32_t idx;
393553a5a1b3Sopenharmony_ci    pa_buffer_attr a;
393653a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
393753a5a1b3Sopenharmony_ci
393853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
393953a5a1b3Sopenharmony_ci    pa_assert(t);
394053a5a1b3Sopenharmony_ci
394153a5a1b3Sopenharmony_ci    memset(&a, 0, sizeof(a));
394253a5a1b3Sopenharmony_ci
394353a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0) {
394453a5a1b3Sopenharmony_ci        protocol_error(c);
394553a5a1b3Sopenharmony_ci        return;
394653a5a1b3Sopenharmony_ci    }
394753a5a1b3Sopenharmony_ci
394853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
394953a5a1b3Sopenharmony_ci
395053a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR) {
395153a5a1b3Sopenharmony_ci        playback_stream *s;
395253a5a1b3Sopenharmony_ci        bool adjust_latency = false, early_requests = false;
395353a5a1b3Sopenharmony_ci
395453a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->output_streams, idx);
395553a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
395653a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
395753a5a1b3Sopenharmony_ci
395853a5a1b3Sopenharmony_ci        if (pa_tagstruct_get(
395953a5a1b3Sopenharmony_ci                    t,
396053a5a1b3Sopenharmony_ci                    PA_TAG_U32, &a.maxlength,
396153a5a1b3Sopenharmony_ci                    PA_TAG_U32, &a.tlength,
396253a5a1b3Sopenharmony_ci                    PA_TAG_U32, &a.prebuf,
396353a5a1b3Sopenharmony_ci                    PA_TAG_U32, &a.minreq,
396453a5a1b3Sopenharmony_ci                    PA_TAG_INVALID) < 0 ||
396553a5a1b3Sopenharmony_ci            (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
396653a5a1b3Sopenharmony_ci            (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
396753a5a1b3Sopenharmony_ci            !pa_tagstruct_eof(t)) {
396853a5a1b3Sopenharmony_ci            protocol_error(c);
396953a5a1b3Sopenharmony_ci            return;
397053a5a1b3Sopenharmony_ci        }
397153a5a1b3Sopenharmony_ci
397253a5a1b3Sopenharmony_ci        s->adjust_latency = adjust_latency;
397353a5a1b3Sopenharmony_ci        s->early_requests = early_requests;
397453a5a1b3Sopenharmony_ci        s->buffer_attr_req = a;
397553a5a1b3Sopenharmony_ci
397653a5a1b3Sopenharmony_ci        fix_playback_buffer_attr(s);
397753a5a1b3Sopenharmony_ci        pa_assert_se(pa_asyncmsgq_send(s->sink_input->sink->asyncmsgq, PA_MSGOBJECT(s->sink_input), SINK_INPUT_MESSAGE_UPDATE_BUFFER_ATTR, NULL, 0, NULL) == 0);
397853a5a1b3Sopenharmony_ci
397953a5a1b3Sopenharmony_ci        reply = reply_new(tag);
398053a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
398153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->buffer_attr.tlength);
398253a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->buffer_attr.prebuf);
398353a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->buffer_attr.minreq);
398453a5a1b3Sopenharmony_ci
398553a5a1b3Sopenharmony_ci        if (c->version >= 13)
398653a5a1b3Sopenharmony_ci            pa_tagstruct_put_usec(reply, s->configured_sink_latency);
398753a5a1b3Sopenharmony_ci
398853a5a1b3Sopenharmony_ci    } else {
398953a5a1b3Sopenharmony_ci        record_stream *s;
399053a5a1b3Sopenharmony_ci        bool adjust_latency = false, early_requests = false;
399153a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR);
399253a5a1b3Sopenharmony_ci
399353a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->record_streams, idx);
399453a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
399553a5a1b3Sopenharmony_ci
399653a5a1b3Sopenharmony_ci        if (pa_tagstruct_get(
399753a5a1b3Sopenharmony_ci                    t,
399853a5a1b3Sopenharmony_ci                    PA_TAG_U32, &a.maxlength,
399953a5a1b3Sopenharmony_ci                    PA_TAG_U32, &a.fragsize,
400053a5a1b3Sopenharmony_ci                    PA_TAG_INVALID) < 0 ||
400153a5a1b3Sopenharmony_ci            (c->version >= 13 && pa_tagstruct_get_boolean(t, &adjust_latency) < 0) ||
400253a5a1b3Sopenharmony_ci            (c->version >= 14 && pa_tagstruct_get_boolean(t, &early_requests) < 0) ||
400353a5a1b3Sopenharmony_ci            !pa_tagstruct_eof(t)) {
400453a5a1b3Sopenharmony_ci            protocol_error(c);
400553a5a1b3Sopenharmony_ci            return;
400653a5a1b3Sopenharmony_ci        }
400753a5a1b3Sopenharmony_ci
400853a5a1b3Sopenharmony_ci        s->adjust_latency = adjust_latency;
400953a5a1b3Sopenharmony_ci        s->early_requests = early_requests;
401053a5a1b3Sopenharmony_ci        s->buffer_attr_req = a;
401153a5a1b3Sopenharmony_ci
401253a5a1b3Sopenharmony_ci        fix_record_buffer_attr_pre(s);
401353a5a1b3Sopenharmony_ci        pa_memblockq_set_maxlength(s->memblockq, s->buffer_attr.maxlength);
401453a5a1b3Sopenharmony_ci        pa_memblockq_get_attr(s->memblockq, &s->buffer_attr);
401553a5a1b3Sopenharmony_ci        fix_record_buffer_attr_post(s);
401653a5a1b3Sopenharmony_ci
401753a5a1b3Sopenharmony_ci        reply = reply_new(tag);
401853a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->buffer_attr.maxlength);
401953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, s->buffer_attr.fragsize);
402053a5a1b3Sopenharmony_ci
402153a5a1b3Sopenharmony_ci        if (c->version >= 13)
402253a5a1b3Sopenharmony_ci            pa_tagstruct_put_usec(reply, s->configured_source_latency);
402353a5a1b3Sopenharmony_ci    }
402453a5a1b3Sopenharmony_ci
402553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
402653a5a1b3Sopenharmony_ci}
402753a5a1b3Sopenharmony_ci
402853a5a1b3Sopenharmony_cistatic void command_update_stream_sample_rate(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
402953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
403053a5a1b3Sopenharmony_ci    uint32_t idx;
403153a5a1b3Sopenharmony_ci    uint32_t rate;
403253a5a1b3Sopenharmony_ci
403353a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
403453a5a1b3Sopenharmony_ci    pa_assert(t);
403553a5a1b3Sopenharmony_ci
403653a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
403753a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &rate) < 0 ||
403853a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
403953a5a1b3Sopenharmony_ci        protocol_error(c);
404053a5a1b3Sopenharmony_ci        return;
404153a5a1b3Sopenharmony_ci    }
404253a5a1b3Sopenharmony_ci
404353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
404453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, pa_sample_rate_valid(rate), tag, PA_ERR_INVALID);
404553a5a1b3Sopenharmony_ci
404653a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE) {
404753a5a1b3Sopenharmony_ci        playback_stream *s;
404853a5a1b3Sopenharmony_ci
404953a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->output_streams, idx);
405053a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
405153a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
405253a5a1b3Sopenharmony_ci
405353a5a1b3Sopenharmony_ci        pa_sink_input_set_rate(s->sink_input, rate);
405453a5a1b3Sopenharmony_ci
405553a5a1b3Sopenharmony_ci    } else {
405653a5a1b3Sopenharmony_ci        record_stream *s;
405753a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE);
405853a5a1b3Sopenharmony_ci
405953a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->record_streams, idx);
406053a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
406153a5a1b3Sopenharmony_ci
406253a5a1b3Sopenharmony_ci        pa_source_output_set_rate(s->source_output, rate);
406353a5a1b3Sopenharmony_ci    }
406453a5a1b3Sopenharmony_ci
406553a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
406653a5a1b3Sopenharmony_ci}
406753a5a1b3Sopenharmony_ci
406853a5a1b3Sopenharmony_cistatic void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
406953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
407053a5a1b3Sopenharmony_ci    uint32_t idx;
407153a5a1b3Sopenharmony_ci    uint32_t mode;
407253a5a1b3Sopenharmony_ci    pa_proplist *p;
407353a5a1b3Sopenharmony_ci
407453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
407553a5a1b3Sopenharmony_ci    pa_assert(t);
407653a5a1b3Sopenharmony_ci
407753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
407853a5a1b3Sopenharmony_ci
407953a5a1b3Sopenharmony_ci    p = pa_proplist_new();
408053a5a1b3Sopenharmony_ci
408153a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_UPDATE_CLIENT_PROPLIST) {
408253a5a1b3Sopenharmony_ci
408353a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &mode) < 0 ||
408453a5a1b3Sopenharmony_ci            pa_tagstruct_get_proplist(t, p) < 0 ||
408553a5a1b3Sopenharmony_ci            !pa_tagstruct_eof(t)) {
408653a5a1b3Sopenharmony_ci            protocol_error(c);
408753a5a1b3Sopenharmony_ci            pa_proplist_free(p);
408853a5a1b3Sopenharmony_ci            return;
408953a5a1b3Sopenharmony_ci        }
409053a5a1b3Sopenharmony_ci
409153a5a1b3Sopenharmony_ci    } else {
409253a5a1b3Sopenharmony_ci
409353a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &idx) < 0 ||
409453a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &mode) < 0 ||
409553a5a1b3Sopenharmony_ci            pa_tagstruct_get_proplist(t, p) < 0 ||
409653a5a1b3Sopenharmony_ci            !pa_tagstruct_eof(t)) {
409753a5a1b3Sopenharmony_ci            protocol_error(c);
409853a5a1b3Sopenharmony_ci            pa_proplist_free(p);
409953a5a1b3Sopenharmony_ci            return;
410053a5a1b3Sopenharmony_ci        }
410153a5a1b3Sopenharmony_ci    }
410253a5a1b3Sopenharmony_ci
410353a5a1b3Sopenharmony_ci    if (!(mode == PA_UPDATE_SET || mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE)) {
410453a5a1b3Sopenharmony_ci        pa_proplist_free(p);
410553a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_INVALID);
410653a5a1b3Sopenharmony_ci    }
410753a5a1b3Sopenharmony_ci
410853a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST) {
410953a5a1b3Sopenharmony_ci        playback_stream *s;
411053a5a1b3Sopenharmony_ci
411153a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->output_streams, idx);
411253a5a1b3Sopenharmony_ci        if (!s || !playback_stream_isinstance(s)) {
411353a5a1b3Sopenharmony_ci            pa_proplist_free(p);
411453a5a1b3Sopenharmony_ci            CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_NOENTITY);
411553a5a1b3Sopenharmony_ci        }
411653a5a1b3Sopenharmony_ci        pa_sink_input_update_proplist(s->sink_input, mode, p);
411753a5a1b3Sopenharmony_ci
411853a5a1b3Sopenharmony_ci    } else if (command == PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST) {
411953a5a1b3Sopenharmony_ci        record_stream *s;
412053a5a1b3Sopenharmony_ci
412153a5a1b3Sopenharmony_ci        if (!(s = pa_idxset_get_by_index(c->record_streams, idx))) {
412253a5a1b3Sopenharmony_ci            pa_proplist_free(p);
412353a5a1b3Sopenharmony_ci            CHECK_VALIDITY(c->pstream, false, tag, PA_ERR_NOENTITY);
412453a5a1b3Sopenharmony_ci        }
412553a5a1b3Sopenharmony_ci        pa_source_output_update_proplist(s->source_output, mode, p);
412653a5a1b3Sopenharmony_ci
412753a5a1b3Sopenharmony_ci    } else {
412853a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_UPDATE_CLIENT_PROPLIST);
412953a5a1b3Sopenharmony_ci
413053a5a1b3Sopenharmony_ci        pa_client_update_proplist(c->client, mode, p);
413153a5a1b3Sopenharmony_ci    }
413253a5a1b3Sopenharmony_ci
413353a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
413453a5a1b3Sopenharmony_ci    pa_proplist_free(p);
413553a5a1b3Sopenharmony_ci}
413653a5a1b3Sopenharmony_ci
413753a5a1b3Sopenharmony_cistatic void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
413853a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
413953a5a1b3Sopenharmony_ci    uint32_t idx;
414053a5a1b3Sopenharmony_ci    unsigned changed = 0;
414153a5a1b3Sopenharmony_ci    pa_proplist *p;
414253a5a1b3Sopenharmony_ci    pa_strlist *l = NULL;
414353a5a1b3Sopenharmony_ci
414453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
414553a5a1b3Sopenharmony_ci    pa_assert(t);
414653a5a1b3Sopenharmony_ci
414753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
414853a5a1b3Sopenharmony_ci
414953a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REMOVE_CLIENT_PROPLIST) {
415053a5a1b3Sopenharmony_ci
415153a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &idx) < 0) {
415253a5a1b3Sopenharmony_ci            protocol_error(c);
415353a5a1b3Sopenharmony_ci            return;
415453a5a1b3Sopenharmony_ci        }
415553a5a1b3Sopenharmony_ci    }
415653a5a1b3Sopenharmony_ci
415753a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
415853a5a1b3Sopenharmony_ci        playback_stream *s;
415953a5a1b3Sopenharmony_ci
416053a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->output_streams, idx);
416153a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
416253a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
416353a5a1b3Sopenharmony_ci
416453a5a1b3Sopenharmony_ci        p = s->sink_input->proplist;
416553a5a1b3Sopenharmony_ci
416653a5a1b3Sopenharmony_ci    } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
416753a5a1b3Sopenharmony_ci        record_stream *s;
416853a5a1b3Sopenharmony_ci
416953a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->record_streams, idx);
417053a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
417153a5a1b3Sopenharmony_ci
417253a5a1b3Sopenharmony_ci        p = s->source_output->proplist;
417353a5a1b3Sopenharmony_ci    } else {
417453a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
417553a5a1b3Sopenharmony_ci
417653a5a1b3Sopenharmony_ci        p = c->client->proplist;
417753a5a1b3Sopenharmony_ci    }
417853a5a1b3Sopenharmony_ci
417953a5a1b3Sopenharmony_ci    for (;;) {
418053a5a1b3Sopenharmony_ci        const char *k;
418153a5a1b3Sopenharmony_ci
418253a5a1b3Sopenharmony_ci        if (pa_tagstruct_gets(t, &k) < 0) {
418353a5a1b3Sopenharmony_ci            protocol_error(c);
418453a5a1b3Sopenharmony_ci            pa_strlist_free(l);
418553a5a1b3Sopenharmony_ci            return;
418653a5a1b3Sopenharmony_ci        }
418753a5a1b3Sopenharmony_ci
418853a5a1b3Sopenharmony_ci        if (!k)
418953a5a1b3Sopenharmony_ci            break;
419053a5a1b3Sopenharmony_ci
419153a5a1b3Sopenharmony_ci        l = pa_strlist_prepend(l, k);
419253a5a1b3Sopenharmony_ci    }
419353a5a1b3Sopenharmony_ci
419453a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
419553a5a1b3Sopenharmony_ci        protocol_error(c);
419653a5a1b3Sopenharmony_ci        pa_strlist_free(l);
419753a5a1b3Sopenharmony_ci        return;
419853a5a1b3Sopenharmony_ci    }
419953a5a1b3Sopenharmony_ci
420053a5a1b3Sopenharmony_ci    for (;;) {
420153a5a1b3Sopenharmony_ci        char *z;
420253a5a1b3Sopenharmony_ci
420353a5a1b3Sopenharmony_ci        l = pa_strlist_pop(l, &z);
420453a5a1b3Sopenharmony_ci
420553a5a1b3Sopenharmony_ci        if (!z)
420653a5a1b3Sopenharmony_ci            break;
420753a5a1b3Sopenharmony_ci
420853a5a1b3Sopenharmony_ci        changed += (unsigned) (pa_proplist_unset(p, z) >= 0);
420953a5a1b3Sopenharmony_ci        pa_xfree(z);
421053a5a1b3Sopenharmony_ci    }
421153a5a1b3Sopenharmony_ci
421253a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
421353a5a1b3Sopenharmony_ci
421453a5a1b3Sopenharmony_ci    if (changed) {
421553a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST) {
421653a5a1b3Sopenharmony_ci            playback_stream *s;
421753a5a1b3Sopenharmony_ci
421853a5a1b3Sopenharmony_ci            s = pa_idxset_get_by_index(c->output_streams, idx);
421953a5a1b3Sopenharmony_ci            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->sink_input->index);
422053a5a1b3Sopenharmony_ci
422153a5a1b3Sopenharmony_ci        } else if (command == PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST) {
422253a5a1b3Sopenharmony_ci            record_stream *s;
422353a5a1b3Sopenharmony_ci
422453a5a1b3Sopenharmony_ci            s = pa_idxset_get_by_index(c->record_streams, idx);
422553a5a1b3Sopenharmony_ci            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, s->source_output->index);
422653a5a1b3Sopenharmony_ci
422753a5a1b3Sopenharmony_ci        } else {
422853a5a1b3Sopenharmony_ci            pa_assert(command == PA_COMMAND_REMOVE_CLIENT_PROPLIST);
422953a5a1b3Sopenharmony_ci            pa_subscription_post(c->protocol->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->client->index);
423053a5a1b3Sopenharmony_ci        }
423153a5a1b3Sopenharmony_ci    }
423253a5a1b3Sopenharmony_ci}
423353a5a1b3Sopenharmony_ci
423453a5a1b3Sopenharmony_cistatic void command_set_default_sink_or_source(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
423553a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
423653a5a1b3Sopenharmony_ci    const char *s;
423753a5a1b3Sopenharmony_ci
423853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
423953a5a1b3Sopenharmony_ci    pa_assert(t);
424053a5a1b3Sopenharmony_ci
424153a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &s) < 0 ||
424253a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
424353a5a1b3Sopenharmony_ci        protocol_error(c);
424453a5a1b3Sopenharmony_ci        return;
424553a5a1b3Sopenharmony_ci    }
424653a5a1b3Sopenharmony_ci
424753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
424853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !s || pa_namereg_is_valid_name(s), tag, PA_ERR_INVALID);
424953a5a1b3Sopenharmony_ci
425053a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_SET_DEFAULT_SOURCE) {
425153a5a1b3Sopenharmony_ci        pa_source *source;
425253a5a1b3Sopenharmony_ci
425353a5a1b3Sopenharmony_ci        source = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SOURCE);
425453a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
425553a5a1b3Sopenharmony_ci
425653a5a1b3Sopenharmony_ci        pa_core_set_configured_default_source(c->protocol->core, source->name);
425753a5a1b3Sopenharmony_ci    } else {
425853a5a1b3Sopenharmony_ci        pa_sink *sink;
425953a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_SET_DEFAULT_SINK);
426053a5a1b3Sopenharmony_ci
426153a5a1b3Sopenharmony_ci        sink = pa_namereg_get(c->protocol->core, s, PA_NAMEREG_SINK);
426253a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
426353a5a1b3Sopenharmony_ci
426453a5a1b3Sopenharmony_ci        pa_core_set_configured_default_sink(c->protocol->core, sink->name);
426553a5a1b3Sopenharmony_ci    }
426653a5a1b3Sopenharmony_ci
426753a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
426853a5a1b3Sopenharmony_ci}
426953a5a1b3Sopenharmony_ci
427053a5a1b3Sopenharmony_cistatic void command_set_stream_name(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
427153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
427253a5a1b3Sopenharmony_ci    uint32_t idx;
427353a5a1b3Sopenharmony_ci    const char *name;
427453a5a1b3Sopenharmony_ci
427553a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
427653a5a1b3Sopenharmony_ci    pa_assert(t);
427753a5a1b3Sopenharmony_ci
427853a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
427953a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
428053a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
428153a5a1b3Sopenharmony_ci        protocol_error(c);
428253a5a1b3Sopenharmony_ci        return;
428353a5a1b3Sopenharmony_ci    }
428453a5a1b3Sopenharmony_ci
428553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
428653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, name && pa_utf8_valid(name), tag, PA_ERR_INVALID);
428753a5a1b3Sopenharmony_ci
428853a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_SET_PLAYBACK_STREAM_NAME) {
428953a5a1b3Sopenharmony_ci        playback_stream *s;
429053a5a1b3Sopenharmony_ci
429153a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->output_streams, idx);
429253a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
429353a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, playback_stream_isinstance(s), tag, PA_ERR_NOENTITY);
429453a5a1b3Sopenharmony_ci
429553a5a1b3Sopenharmony_ci        pa_sink_input_set_property(s->sink_input, PA_PROP_MEDIA_NAME, name);
429653a5a1b3Sopenharmony_ci
429753a5a1b3Sopenharmony_ci    } else {
429853a5a1b3Sopenharmony_ci        record_stream *s;
429953a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_SET_RECORD_STREAM_NAME);
430053a5a1b3Sopenharmony_ci
430153a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->record_streams, idx);
430253a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
430353a5a1b3Sopenharmony_ci
430453a5a1b3Sopenharmony_ci        pa_source_output_set_property(s->source_output, PA_PROP_MEDIA_NAME, name);
430553a5a1b3Sopenharmony_ci    }
430653a5a1b3Sopenharmony_ci
430753a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
430853a5a1b3Sopenharmony_ci}
430953a5a1b3Sopenharmony_ci
431053a5a1b3Sopenharmony_cistatic void command_kill(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
431153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
431253a5a1b3Sopenharmony_ci    uint32_t idx;
431353a5a1b3Sopenharmony_ci
431453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
431553a5a1b3Sopenharmony_ci    pa_assert(t);
431653a5a1b3Sopenharmony_ci
431753a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
431853a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
431953a5a1b3Sopenharmony_ci        protocol_error(c);
432053a5a1b3Sopenharmony_ci        return;
432153a5a1b3Sopenharmony_ci    }
432253a5a1b3Sopenharmony_ci
432353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
432453a5a1b3Sopenharmony_ci
432553a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_KILL_CLIENT) {
432653a5a1b3Sopenharmony_ci        pa_client *client;
432753a5a1b3Sopenharmony_ci
432853a5a1b3Sopenharmony_ci        client = pa_idxset_get_by_index(c->protocol->core->clients, idx);
432953a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, client, tag, PA_ERR_NOENTITY);
433053a5a1b3Sopenharmony_ci
433153a5a1b3Sopenharmony_ci        pa_native_connection_ref(c);
433253a5a1b3Sopenharmony_ci        pa_client_kill(client);
433353a5a1b3Sopenharmony_ci
433453a5a1b3Sopenharmony_ci    } else if (command == PA_COMMAND_KILL_SINK_INPUT) {
433553a5a1b3Sopenharmony_ci        pa_sink_input *s;
433653a5a1b3Sopenharmony_ci
433753a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
433853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
433953a5a1b3Sopenharmony_ci
434053a5a1b3Sopenharmony_ci        pa_native_connection_ref(c);
434153a5a1b3Sopenharmony_ci        pa_sink_input_kill(s);
434253a5a1b3Sopenharmony_ci    } else {
434353a5a1b3Sopenharmony_ci        pa_source_output *s;
434453a5a1b3Sopenharmony_ci
434553a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_KILL_SOURCE_OUTPUT);
434653a5a1b3Sopenharmony_ci
434753a5a1b3Sopenharmony_ci        s = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
434853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, s, tag, PA_ERR_NOENTITY);
434953a5a1b3Sopenharmony_ci
435053a5a1b3Sopenharmony_ci        pa_native_connection_ref(c);
435153a5a1b3Sopenharmony_ci        pa_source_output_kill(s);
435253a5a1b3Sopenharmony_ci    }
435353a5a1b3Sopenharmony_ci
435453a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
435553a5a1b3Sopenharmony_ci    pa_native_connection_unref(c);
435653a5a1b3Sopenharmony_ci}
435753a5a1b3Sopenharmony_ci
435853a5a1b3Sopenharmony_cistatic void command_load_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
435953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
436053a5a1b3Sopenharmony_ci    pa_module *m;
436153a5a1b3Sopenharmony_ci    const char *name, *argument;
436253a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
436353a5a1b3Sopenharmony_ci
436453a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
436553a5a1b3Sopenharmony_ci    pa_assert(t);
436653a5a1b3Sopenharmony_ci
436753a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &name) < 0 ||
436853a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &argument) < 0 ||
436953a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
437053a5a1b3Sopenharmony_ci        protocol_error(c);
437153a5a1b3Sopenharmony_ci        return;
437253a5a1b3Sopenharmony_ci    }
437353a5a1b3Sopenharmony_ci
437453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
437553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, name && *name && pa_utf8_valid(name) && !strchr(name, '/'), tag, PA_ERR_INVALID);
437653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !argument || pa_utf8_valid(argument), tag, PA_ERR_INVALID);
437753a5a1b3Sopenharmony_ci
437853a5a1b3Sopenharmony_ci    if (pa_module_load(&m, c->protocol->core, name, argument) < 0) {
437953a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, PA_ERR_MODINITFAILED);
438053a5a1b3Sopenharmony_ci        return;
438153a5a1b3Sopenharmony_ci    }
438253a5a1b3Sopenharmony_ci
438353a5a1b3Sopenharmony_ci    reply = reply_new(tag);
438453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, m->index);
438553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
438653a5a1b3Sopenharmony_ci}
438753a5a1b3Sopenharmony_ci
438853a5a1b3Sopenharmony_cistatic void command_unload_module(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
438953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
439053a5a1b3Sopenharmony_ci    uint32_t idx;
439153a5a1b3Sopenharmony_ci    pa_module *m;
439253a5a1b3Sopenharmony_ci
439353a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
439453a5a1b3Sopenharmony_ci    pa_assert(t);
439553a5a1b3Sopenharmony_ci
439653a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
439753a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
439853a5a1b3Sopenharmony_ci        protocol_error(c);
439953a5a1b3Sopenharmony_ci        return;
440053a5a1b3Sopenharmony_ci    }
440153a5a1b3Sopenharmony_ci
440253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
440353a5a1b3Sopenharmony_ci    m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
440453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOENTITY);
440553a5a1b3Sopenharmony_ci
440653a5a1b3Sopenharmony_ci    pa_module_unload_request(m, false);
440753a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
440853a5a1b3Sopenharmony_ci}
440953a5a1b3Sopenharmony_ci
441053a5a1b3Sopenharmony_cistatic void command_move_stream(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
441153a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
441253a5a1b3Sopenharmony_ci    uint32_t idx = PA_INVALID_INDEX, idx_device = PA_INVALID_INDEX;
441353a5a1b3Sopenharmony_ci    const char *name_device = NULL;
441453a5a1b3Sopenharmony_ci
441553a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
441653a5a1b3Sopenharmony_ci    pa_assert(t);
441753a5a1b3Sopenharmony_ci
441853a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
441953a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &idx_device) < 0 ||
442053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name_device) < 0 ||
442153a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
442253a5a1b3Sopenharmony_ci        protocol_error(c);
442353a5a1b3Sopenharmony_ci        return;
442453a5a1b3Sopenharmony_ci    }
442553a5a1b3Sopenharmony_ci
442653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
442753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
442853a5a1b3Sopenharmony_ci
442953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name_device || pa_namereg_is_valid_name_or_wildcard(name_device, command == PA_COMMAND_MOVE_SINK_INPUT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
443053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx_device != PA_INVALID_INDEX) ^ (name_device != NULL), tag, PA_ERR_INVALID);
443153a5a1b3Sopenharmony_ci
443253a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_MOVE_SINK_INPUT) {
443353a5a1b3Sopenharmony_ci        pa_sink_input *si = NULL;
443453a5a1b3Sopenharmony_ci        pa_sink *sink = NULL;
443553a5a1b3Sopenharmony_ci
443653a5a1b3Sopenharmony_ci        si = pa_idxset_get_by_index(c->protocol->core->sink_inputs, idx);
443753a5a1b3Sopenharmony_ci
443853a5a1b3Sopenharmony_ci        if (idx_device != PA_INVALID_INDEX)
443953a5a1b3Sopenharmony_ci            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx_device);
444053a5a1b3Sopenharmony_ci        else
444153a5a1b3Sopenharmony_ci            sink = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SINK);
444253a5a1b3Sopenharmony_ci
444353a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, si && sink, tag, PA_ERR_NOENTITY);
444453a5a1b3Sopenharmony_ci
444553a5a1b3Sopenharmony_ci        if (pa_sink_input_move_to(si, sink, true) < 0) {
444653a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
444753a5a1b3Sopenharmony_ci            return;
444853a5a1b3Sopenharmony_ci        }
444953a5a1b3Sopenharmony_ci    } else {
445053a5a1b3Sopenharmony_ci        pa_source_output *so = NULL;
445153a5a1b3Sopenharmony_ci        pa_source *source;
445253a5a1b3Sopenharmony_ci
445353a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_MOVE_SOURCE_OUTPUT);
445453a5a1b3Sopenharmony_ci
445553a5a1b3Sopenharmony_ci        so = pa_idxset_get_by_index(c->protocol->core->source_outputs, idx);
445653a5a1b3Sopenharmony_ci
445753a5a1b3Sopenharmony_ci        if (idx_device != PA_INVALID_INDEX)
445853a5a1b3Sopenharmony_ci            source = pa_idxset_get_by_index(c->protocol->core->sources, idx_device);
445953a5a1b3Sopenharmony_ci        else
446053a5a1b3Sopenharmony_ci            source = pa_namereg_get(c->protocol->core, name_device, PA_NAMEREG_SOURCE);
446153a5a1b3Sopenharmony_ci
446253a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, so && source, tag, PA_ERR_NOENTITY);
446353a5a1b3Sopenharmony_ci
446453a5a1b3Sopenharmony_ci        if (pa_source_output_move_to(so, source, true) < 0) {
446553a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
446653a5a1b3Sopenharmony_ci            return;
446753a5a1b3Sopenharmony_ci        }
446853a5a1b3Sopenharmony_ci    }
446953a5a1b3Sopenharmony_ci
447053a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
447153a5a1b3Sopenharmony_ci}
447253a5a1b3Sopenharmony_ci
447353a5a1b3Sopenharmony_cistatic void command_suspend(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
447453a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
447553a5a1b3Sopenharmony_ci    uint32_t idx = PA_INVALID_INDEX;
447653a5a1b3Sopenharmony_ci    const char *name = NULL;
447753a5a1b3Sopenharmony_ci    bool b;
447853a5a1b3Sopenharmony_ci
447953a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
448053a5a1b3Sopenharmony_ci    pa_assert(t);
448153a5a1b3Sopenharmony_ci
448253a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
448353a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
448453a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &b) < 0 ||
448553a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
448653a5a1b3Sopenharmony_ci        protocol_error(c);
448753a5a1b3Sopenharmony_ci        return;
448853a5a1b3Sopenharmony_ci    }
448953a5a1b3Sopenharmony_ci
449053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
449153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SUSPEND_SINK ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE) || *name == 0, tag, PA_ERR_INVALID);
449253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
449353a5a1b3Sopenharmony_ci
449453a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_SUSPEND_SINK) {
449553a5a1b3Sopenharmony_ci
449653a5a1b3Sopenharmony_ci        if (idx == PA_INVALID_INDEX && name && !*name) {
449753a5a1b3Sopenharmony_ci
449853a5a1b3Sopenharmony_ci            pa_log_debug("%s all sinks", b ? "Suspending" : "Resuming");
449953a5a1b3Sopenharmony_ci
450053a5a1b3Sopenharmony_ci            if (pa_sink_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
450153a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
450253a5a1b3Sopenharmony_ci                return;
450353a5a1b3Sopenharmony_ci            }
450453a5a1b3Sopenharmony_ci        } else {
450553a5a1b3Sopenharmony_ci            pa_sink *sink = NULL;
450653a5a1b3Sopenharmony_ci
450753a5a1b3Sopenharmony_ci            if (idx != PA_INVALID_INDEX)
450853a5a1b3Sopenharmony_ci                sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
450953a5a1b3Sopenharmony_ci            else
451053a5a1b3Sopenharmony_ci                sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
451153a5a1b3Sopenharmony_ci
451253a5a1b3Sopenharmony_ci            CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
451353a5a1b3Sopenharmony_ci
451453a5a1b3Sopenharmony_ci            pa_log_debug("%s of sink %s requested by client %" PRIu32 ".",
451553a5a1b3Sopenharmony_ci                         b ? "Suspending" : "Resuming", sink->name, c->client->index);
451653a5a1b3Sopenharmony_ci
451753a5a1b3Sopenharmony_ci            if (pa_sink_suspend(sink, b, PA_SUSPEND_USER) < 0) {
451853a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
451953a5a1b3Sopenharmony_ci                return;
452053a5a1b3Sopenharmony_ci            }
452153a5a1b3Sopenharmony_ci        }
452253a5a1b3Sopenharmony_ci    } else {
452353a5a1b3Sopenharmony_ci
452453a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_SUSPEND_SOURCE);
452553a5a1b3Sopenharmony_ci
452653a5a1b3Sopenharmony_ci        if (idx == PA_INVALID_INDEX && name && !*name) {
452753a5a1b3Sopenharmony_ci
452853a5a1b3Sopenharmony_ci            pa_log_debug("%s all sources", b ? "Suspending" : "Resuming");
452953a5a1b3Sopenharmony_ci
453053a5a1b3Sopenharmony_ci            if (pa_source_suspend_all(c->protocol->core, b, PA_SUSPEND_USER) < 0) {
453153a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
453253a5a1b3Sopenharmony_ci                return;
453353a5a1b3Sopenharmony_ci            }
453453a5a1b3Sopenharmony_ci
453553a5a1b3Sopenharmony_ci        } else {
453653a5a1b3Sopenharmony_ci            pa_source *source;
453753a5a1b3Sopenharmony_ci
453853a5a1b3Sopenharmony_ci            if (idx != PA_INVALID_INDEX)
453953a5a1b3Sopenharmony_ci                source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
454053a5a1b3Sopenharmony_ci            else
454153a5a1b3Sopenharmony_ci                source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
454253a5a1b3Sopenharmony_ci
454353a5a1b3Sopenharmony_ci            CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
454453a5a1b3Sopenharmony_ci
454553a5a1b3Sopenharmony_ci            pa_log_debug("%s of source %s requested by client %" PRIu32 ".",
454653a5a1b3Sopenharmony_ci                         b ? "Suspending" : "Resuming", source->name, c->client->index);
454753a5a1b3Sopenharmony_ci
454853a5a1b3Sopenharmony_ci            if (pa_source_suspend(source, b, PA_SUSPEND_USER) < 0) {
454953a5a1b3Sopenharmony_ci                pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
455053a5a1b3Sopenharmony_ci                return;
455153a5a1b3Sopenharmony_ci            }
455253a5a1b3Sopenharmony_ci        }
455353a5a1b3Sopenharmony_ci    }
455453a5a1b3Sopenharmony_ci
455553a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
455653a5a1b3Sopenharmony_ci}
455753a5a1b3Sopenharmony_ci
455853a5a1b3Sopenharmony_cistatic void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
455953a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
456053a5a1b3Sopenharmony_ci    uint32_t idx = PA_INVALID_INDEX;
456153a5a1b3Sopenharmony_ci    const char *name = NULL;
456253a5a1b3Sopenharmony_ci    pa_module *m;
456353a5a1b3Sopenharmony_ci    pa_native_protocol_ext_cb_t cb;
456453a5a1b3Sopenharmony_ci
456553a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
456653a5a1b3Sopenharmony_ci    pa_assert(t);
456753a5a1b3Sopenharmony_ci
456853a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
456953a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0) {
457053a5a1b3Sopenharmony_ci        protocol_error(c);
457153a5a1b3Sopenharmony_ci        return;
457253a5a1b3Sopenharmony_ci    }
457353a5a1b3Sopenharmony_ci
457453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
457553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name || pa_utf8_valid(name), tag, PA_ERR_INVALID);
457653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
457753a5a1b3Sopenharmony_ci
457853a5a1b3Sopenharmony_ci    if (idx != PA_INVALID_INDEX)
457953a5a1b3Sopenharmony_ci        m = pa_idxset_get_by_index(c->protocol->core->modules, idx);
458053a5a1b3Sopenharmony_ci    else
458153a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(m, c->protocol->core->modules, idx)
458253a5a1b3Sopenharmony_ci            if (pa_streq(name, m->name))
458353a5a1b3Sopenharmony_ci                break;
458453a5a1b3Sopenharmony_ci
458553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, m, tag, PA_ERR_NOEXTENSION);
458653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, m->load_once || idx != PA_INVALID_INDEX, tag, PA_ERR_INVALID);
458753a5a1b3Sopenharmony_ci
458853a5a1b3Sopenharmony_ci    cb = pa_hashmap_get(c->protocol->extensions, m);
458953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, cb, tag, PA_ERR_NOEXTENSION);
459053a5a1b3Sopenharmony_ci
459153a5a1b3Sopenharmony_ci    if (cb(c->protocol, m, c, tag, t) < 0)
459253a5a1b3Sopenharmony_ci        protocol_error(c);
459353a5a1b3Sopenharmony_ci}
459453a5a1b3Sopenharmony_ci
459553a5a1b3Sopenharmony_ci/* Send message to an object which registered a handler. Result must be returned as string. */
459653a5a1b3Sopenharmony_cistatic void command_send_object_message(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
459753a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
459853a5a1b3Sopenharmony_ci    const char *object_path = NULL;
459953a5a1b3Sopenharmony_ci    const char *message = NULL;
460053a5a1b3Sopenharmony_ci    const char *message_parameters = NULL;
460153a5a1b3Sopenharmony_ci    const char *client_name;
460253a5a1b3Sopenharmony_ci    char *response = NULL;
460353a5a1b3Sopenharmony_ci    int ret;
460453a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
460553a5a1b3Sopenharmony_ci
460653a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
460753a5a1b3Sopenharmony_ci    pa_assert(t);
460853a5a1b3Sopenharmony_ci
460953a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &object_path) < 0 ||
461053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &message) < 0 ||
461153a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &message_parameters) < 0 ||
461253a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
461353a5a1b3Sopenharmony_ci        protocol_error(c);
461453a5a1b3Sopenharmony_ci        return;
461553a5a1b3Sopenharmony_ci    }
461653a5a1b3Sopenharmony_ci
461753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
461853a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, object_path != NULL, tag, PA_ERR_INVALID);
461953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, pa_utf8_valid(object_path), tag, PA_ERR_INVALID);
462053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, message != NULL, tag, PA_ERR_INVALID);
462153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, pa_utf8_valid(message), tag, PA_ERR_INVALID);
462253a5a1b3Sopenharmony_ci    if (message_parameters)
462353a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, pa_utf8_valid(message_parameters), tag, PA_ERR_INVALID);
462453a5a1b3Sopenharmony_ci
462553a5a1b3Sopenharmony_ci    client_name = pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_PROCESS_BINARY));
462653a5a1b3Sopenharmony_ci    pa_log_debug("Client %s sent message %s to path %s", client_name, message, object_path);
462753a5a1b3Sopenharmony_ci    if (message_parameters)
462853a5a1b3Sopenharmony_ci        pa_log_debug("Message parameters: %s", message_parameters);
462953a5a1b3Sopenharmony_ci
463053a5a1b3Sopenharmony_ci    ret = pa_message_handler_send_message(c->protocol->core, object_path, message, message_parameters, &response);
463153a5a1b3Sopenharmony_ci
463253a5a1b3Sopenharmony_ci    if (ret < 0) {
463353a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, -ret);
463453a5a1b3Sopenharmony_ci        return;
463553a5a1b3Sopenharmony_ci    }
463653a5a1b3Sopenharmony_ci
463753a5a1b3Sopenharmony_ci    reply = reply_new(tag);
463853a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, response);
463953a5a1b3Sopenharmony_ci    pa_xfree(response);
464053a5a1b3Sopenharmony_ci
464153a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, reply);
464253a5a1b3Sopenharmony_ci}
464353a5a1b3Sopenharmony_ci
464453a5a1b3Sopenharmony_cistatic void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
464553a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
464653a5a1b3Sopenharmony_ci    uint32_t idx = PA_INVALID_INDEX;
464753a5a1b3Sopenharmony_ci    const char *name = NULL, *profile_name = NULL;
464853a5a1b3Sopenharmony_ci    pa_card *card = NULL;
464953a5a1b3Sopenharmony_ci    pa_card_profile *profile;
465053a5a1b3Sopenharmony_ci    int ret;
465153a5a1b3Sopenharmony_ci
465253a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
465353a5a1b3Sopenharmony_ci    pa_assert(t);
465453a5a1b3Sopenharmony_ci
465553a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
465653a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
465753a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &profile_name) < 0 ||
465853a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
465953a5a1b3Sopenharmony_ci        protocol_error(c);
466053a5a1b3Sopenharmony_ci        return;
466153a5a1b3Sopenharmony_ci    }
466253a5a1b3Sopenharmony_ci
466353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
466453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
466553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
466653a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, profile_name, tag, PA_ERR_INVALID);
466753a5a1b3Sopenharmony_ci
466853a5a1b3Sopenharmony_ci    if (idx != PA_INVALID_INDEX)
466953a5a1b3Sopenharmony_ci        card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
467053a5a1b3Sopenharmony_ci    else
467153a5a1b3Sopenharmony_ci        card = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_CARD);
467253a5a1b3Sopenharmony_ci
467353a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
467453a5a1b3Sopenharmony_ci
467553a5a1b3Sopenharmony_ci    profile = pa_hashmap_get(card->profiles, profile_name);
467653a5a1b3Sopenharmony_ci
467753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, profile, tag, PA_ERR_NOENTITY);
467853a5a1b3Sopenharmony_ci
467953a5a1b3Sopenharmony_ci    pa_log_info("Application \"%s\" requests card profile change. card = %s, profile = %s",
468053a5a1b3Sopenharmony_ci                pa_strnull(pa_proplist_gets(c->client->proplist, PA_PROP_APPLICATION_NAME)),
468153a5a1b3Sopenharmony_ci                card->name,
468253a5a1b3Sopenharmony_ci                profile->name);
468353a5a1b3Sopenharmony_ci
468453a5a1b3Sopenharmony_ci    if ((ret = pa_card_set_profile(card, profile, true)) < 0) {
468553a5a1b3Sopenharmony_ci        pa_pstream_send_error(c->pstream, tag, -ret);
468653a5a1b3Sopenharmony_ci        return;
468753a5a1b3Sopenharmony_ci    }
468853a5a1b3Sopenharmony_ci
468953a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
469053a5a1b3Sopenharmony_ci}
469153a5a1b3Sopenharmony_ci
469253a5a1b3Sopenharmony_cistatic void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
469353a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
469453a5a1b3Sopenharmony_ci    uint32_t idx = PA_INVALID_INDEX;
469553a5a1b3Sopenharmony_ci    const char *name = NULL, *port = NULL;
469653a5a1b3Sopenharmony_ci    int ret;
469753a5a1b3Sopenharmony_ci
469853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
469953a5a1b3Sopenharmony_ci    pa_assert(t);
470053a5a1b3Sopenharmony_ci
470153a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
470253a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
470353a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &port) < 0 ||
470453a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
470553a5a1b3Sopenharmony_ci        protocol_error(c);
470653a5a1b3Sopenharmony_ci        return;
470753a5a1b3Sopenharmony_ci    }
470853a5a1b3Sopenharmony_ci
470953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
471053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name_or_wildcard(name, command == PA_COMMAND_SET_SINK_PORT ? PA_NAMEREG_SINK : PA_NAMEREG_SOURCE), tag, PA_ERR_INVALID);
471153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (name != NULL), tag, PA_ERR_INVALID);
471253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_INVALID);
471353a5a1b3Sopenharmony_ci
471453a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_SET_SINK_PORT) {
471553a5a1b3Sopenharmony_ci        pa_sink *sink;
471653a5a1b3Sopenharmony_ci
471753a5a1b3Sopenharmony_ci        if (idx != PA_INVALID_INDEX)
471853a5a1b3Sopenharmony_ci            sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
471953a5a1b3Sopenharmony_ci        else
472053a5a1b3Sopenharmony_ci            sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
472153a5a1b3Sopenharmony_ci
472253a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
472353a5a1b3Sopenharmony_ci
472453a5a1b3Sopenharmony_ci        if ((ret = pa_sink_set_port(sink, port, true)) < 0) {
472553a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, -ret);
472653a5a1b3Sopenharmony_ci            return;
472753a5a1b3Sopenharmony_ci        }
472853a5a1b3Sopenharmony_ci    } else {
472953a5a1b3Sopenharmony_ci        pa_source *source;
473053a5a1b3Sopenharmony_ci
473153a5a1b3Sopenharmony_ci        pa_assert(command == PA_COMMAND_SET_SOURCE_PORT);
473253a5a1b3Sopenharmony_ci
473353a5a1b3Sopenharmony_ci        if (idx != PA_INVALID_INDEX)
473453a5a1b3Sopenharmony_ci            source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
473553a5a1b3Sopenharmony_ci        else
473653a5a1b3Sopenharmony_ci            source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
473753a5a1b3Sopenharmony_ci
473853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
473953a5a1b3Sopenharmony_ci
474053a5a1b3Sopenharmony_ci        if ((ret = pa_source_set_port(source, port, true)) < 0) {
474153a5a1b3Sopenharmony_ci            pa_pstream_send_error(c->pstream, tag, -ret);
474253a5a1b3Sopenharmony_ci            return;
474353a5a1b3Sopenharmony_ci        }
474453a5a1b3Sopenharmony_ci    }
474553a5a1b3Sopenharmony_ci
474653a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
474753a5a1b3Sopenharmony_ci}
474853a5a1b3Sopenharmony_ci
474953a5a1b3Sopenharmony_cistatic void command_set_port_latency_offset(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
475053a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
475153a5a1b3Sopenharmony_ci    const char *port_name, *card_name;
475253a5a1b3Sopenharmony_ci    uint32_t idx = PA_INVALID_INDEX;
475353a5a1b3Sopenharmony_ci    int64_t offset;
475453a5a1b3Sopenharmony_ci    pa_card *card = NULL;
475553a5a1b3Sopenharmony_ci    pa_device_port *port = NULL;
475653a5a1b3Sopenharmony_ci
475753a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
475853a5a1b3Sopenharmony_ci    pa_assert(t);
475953a5a1b3Sopenharmony_ci
476053a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
476153a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &card_name) < 0 ||
476253a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &port_name) < 0 ||
476353a5a1b3Sopenharmony_ci        pa_tagstruct_gets64(t, &offset) < 0 ||
476453a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
476553a5a1b3Sopenharmony_ci        protocol_error(c);
476653a5a1b3Sopenharmony_ci        return;
476753a5a1b3Sopenharmony_ci    }
476853a5a1b3Sopenharmony_ci
476953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
477053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, !card_name || pa_namereg_is_valid_name(card_name), tag, PA_ERR_INVALID);
477153a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, (idx != PA_INVALID_INDEX) ^ (card_name != NULL), tag, PA_ERR_INVALID);
477253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, port_name, tag, PA_ERR_INVALID);
477353a5a1b3Sopenharmony_ci
477453a5a1b3Sopenharmony_ci    if (idx != PA_INVALID_INDEX)
477553a5a1b3Sopenharmony_ci        card = pa_idxset_get_by_index(c->protocol->core->cards, idx);
477653a5a1b3Sopenharmony_ci    else
477753a5a1b3Sopenharmony_ci        card = pa_namereg_get(c->protocol->core, card_name, PA_NAMEREG_CARD);
477853a5a1b3Sopenharmony_ci
477953a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
478053a5a1b3Sopenharmony_ci
478153a5a1b3Sopenharmony_ci    port = pa_hashmap_get(card->ports, port_name);
478253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->pstream, port, tag, PA_ERR_NOENTITY);
478353a5a1b3Sopenharmony_ci
478453a5a1b3Sopenharmony_ci    pa_device_port_set_latency_offset(port, offset);
478553a5a1b3Sopenharmony_ci
478653a5a1b3Sopenharmony_ci    pa_pstream_send_simple_ack(c->pstream, tag);
478753a5a1b3Sopenharmony_ci}
478853a5a1b3Sopenharmony_ci
478953a5a1b3Sopenharmony_cistatic const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
479053a5a1b3Sopenharmony_ci    [PA_COMMAND_ERROR] = NULL,
479153a5a1b3Sopenharmony_ci    [PA_COMMAND_TIMEOUT] = NULL,
479253a5a1b3Sopenharmony_ci    [PA_COMMAND_REPLY] = NULL,
479353a5a1b3Sopenharmony_ci    [PA_COMMAND_CREATE_PLAYBACK_STREAM] = command_create_playback_stream,
479453a5a1b3Sopenharmony_ci    [PA_COMMAND_DELETE_PLAYBACK_STREAM] = command_delete_stream,
479553a5a1b3Sopenharmony_ci    [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = command_drain_playback_stream,
479653a5a1b3Sopenharmony_ci    [PA_COMMAND_CREATE_RECORD_STREAM] = command_create_record_stream,
479753a5a1b3Sopenharmony_ci    [PA_COMMAND_DELETE_RECORD_STREAM] = command_delete_stream,
479853a5a1b3Sopenharmony_ci    [PA_COMMAND_AUTH] = command_auth,
479953a5a1b3Sopenharmony_ci    [PA_COMMAND_REQUEST] = NULL,
480053a5a1b3Sopenharmony_ci    [PA_COMMAND_EXIT] = command_exit,
480153a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_CLIENT_NAME] = command_set_client_name,
480253a5a1b3Sopenharmony_ci    [PA_COMMAND_LOOKUP_SINK] = command_lookup,
480353a5a1b3Sopenharmony_ci    [PA_COMMAND_LOOKUP_SOURCE] = command_lookup,
480453a5a1b3Sopenharmony_ci    [PA_COMMAND_STAT] = command_stat,
480553a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_PLAYBACK_LATENCY] = command_get_playback_latency,
480653a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_RECORD_LATENCY] = command_get_record_latency,
480753a5a1b3Sopenharmony_ci    [PA_COMMAND_CREATE_UPLOAD_STREAM] = command_create_upload_stream,
480853a5a1b3Sopenharmony_ci    [PA_COMMAND_DELETE_UPLOAD_STREAM] = command_delete_stream,
480953a5a1b3Sopenharmony_ci    [PA_COMMAND_FINISH_UPLOAD_STREAM] = command_finish_upload_stream,
481053a5a1b3Sopenharmony_ci    [PA_COMMAND_PLAY_SAMPLE] = command_play_sample,
481153a5a1b3Sopenharmony_ci    [PA_COMMAND_REMOVE_SAMPLE] = command_remove_sample,
481253a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SINK_INFO] = command_get_info,
481353a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SOURCE_INFO] = command_get_info,
481453a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_CLIENT_INFO] = command_get_info,
481553a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_CARD_INFO] = command_get_info,
481653a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_MODULE_INFO] = command_get_info,
481753a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SINK_INPUT_INFO] = command_get_info,
481853a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = command_get_info,
481953a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SAMPLE_INFO] = command_get_info,
482053a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SINK_INFO_LIST] = command_get_info_list,
482153a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SOURCE_INFO_LIST] = command_get_info_list,
482253a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_MODULE_INFO_LIST] = command_get_info_list,
482353a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_CLIENT_INFO_LIST] = command_get_info_list,
482453a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_CARD_INFO_LIST] = command_get_info_list,
482553a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = command_get_info_list,
482653a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = command_get_info_list,
482753a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SAMPLE_INFO_LIST] = command_get_info_list,
482853a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_SERVER_INFO] = command_get_server_info,
482953a5a1b3Sopenharmony_ci    [PA_COMMAND_SUBSCRIBE] = command_subscribe,
483053a5a1b3Sopenharmony_ci
483153a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SINK_VOLUME] = command_set_volume,
483253a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SINK_INPUT_VOLUME] = command_set_volume,
483353a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SOURCE_VOLUME] = command_set_volume,
483453a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME] = command_set_volume,
483553a5a1b3Sopenharmony_ci
483653a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SINK_MUTE] = command_set_mute,
483753a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SINK_INPUT_MUTE] = command_set_mute,
483853a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SOURCE_MUTE] = command_set_mute,
483953a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SOURCE_OUTPUT_MUTE] = command_set_mute,
484053a5a1b3Sopenharmony_ci
484153a5a1b3Sopenharmony_ci    [PA_COMMAND_SUSPEND_SINK] = command_suspend,
484253a5a1b3Sopenharmony_ci    [PA_COMMAND_SUSPEND_SOURCE] = command_suspend,
484353a5a1b3Sopenharmony_ci
484453a5a1b3Sopenharmony_ci    [PA_COMMAND_CORK_PLAYBACK_STREAM] = command_cork_playback_stream,
484553a5a1b3Sopenharmony_ci    [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
484653a5a1b3Sopenharmony_ci    [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
484753a5a1b3Sopenharmony_ci    [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = command_trigger_or_flush_or_prebuf_playback_stream,
484853a5a1b3Sopenharmony_ci
484953a5a1b3Sopenharmony_ci    [PA_COMMAND_CORK_RECORD_STREAM] = command_cork_record_stream,
485053a5a1b3Sopenharmony_ci    [PA_COMMAND_FLUSH_RECORD_STREAM] = command_flush_record_stream,
485153a5a1b3Sopenharmony_ci
485253a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_DEFAULT_SINK] = command_set_default_sink_or_source,
485353a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_DEFAULT_SOURCE] = command_set_default_sink_or_source,
485453a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = command_set_stream_name,
485553a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_RECORD_STREAM_NAME] = command_set_stream_name,
485653a5a1b3Sopenharmony_ci    [PA_COMMAND_KILL_CLIENT] = command_kill,
485753a5a1b3Sopenharmony_ci    [PA_COMMAND_KILL_SINK_INPUT] = command_kill,
485853a5a1b3Sopenharmony_ci    [PA_COMMAND_KILL_SOURCE_OUTPUT] = command_kill,
485953a5a1b3Sopenharmony_ci    [PA_COMMAND_LOAD_MODULE] = command_load_module,
486053a5a1b3Sopenharmony_ci    [PA_COMMAND_UNLOAD_MODULE] = command_unload_module,
486153a5a1b3Sopenharmony_ci
486253a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = NULL,
486353a5a1b3Sopenharmony_ci    [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = NULL,
486453a5a1b3Sopenharmony_ci    [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = NULL,
486553a5a1b3Sopenharmony_ci    [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = NULL,
486653a5a1b3Sopenharmony_ci
486753a5a1b3Sopenharmony_ci    [PA_COMMAND_MOVE_SINK_INPUT] = command_move_stream,
486853a5a1b3Sopenharmony_ci    [PA_COMMAND_MOVE_SOURCE_OUTPUT] = command_move_stream,
486953a5a1b3Sopenharmony_ci
487053a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
487153a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = command_set_stream_buffer_attr,
487253a5a1b3Sopenharmony_ci
487353a5a1b3Sopenharmony_ci    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
487453a5a1b3Sopenharmony_ci    [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = command_update_stream_sample_rate,
487553a5a1b3Sopenharmony_ci
487653a5a1b3Sopenharmony_ci    [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = command_update_proplist,
487753a5a1b3Sopenharmony_ci    [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = command_update_proplist,
487853a5a1b3Sopenharmony_ci    [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = command_update_proplist,
487953a5a1b3Sopenharmony_ci
488053a5a1b3Sopenharmony_ci    [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = command_remove_proplist,
488153a5a1b3Sopenharmony_ci    [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = command_remove_proplist,
488253a5a1b3Sopenharmony_ci    [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = command_remove_proplist,
488353a5a1b3Sopenharmony_ci
488453a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
488553a5a1b3Sopenharmony_ci
488653a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
488753a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
488853a5a1b3Sopenharmony_ci
488953a5a1b3Sopenharmony_ci    [PA_COMMAND_SET_PORT_LATENCY_OFFSET] = command_set_port_latency_offset,
489053a5a1b3Sopenharmony_ci
489153a5a1b3Sopenharmony_ci    [PA_COMMAND_ENABLE_SRBCHANNEL] = command_enable_srbchannel,
489253a5a1b3Sopenharmony_ci
489353a5a1b3Sopenharmony_ci    [PA_COMMAND_REGISTER_MEMFD_SHMID] = command_register_memfd_shmid,
489453a5a1b3Sopenharmony_ci
489553a5a1b3Sopenharmony_ci    [PA_COMMAND_SEND_OBJECT_MESSAGE] = command_send_object_message,
489653a5a1b3Sopenharmony_ci
489753a5a1b3Sopenharmony_ci    [PA_COMMAND_EXTENSION] = command_extension
489853a5a1b3Sopenharmony_ci};
489953a5a1b3Sopenharmony_ci
490053a5a1b3Sopenharmony_ci/*** pstream callbacks ***/
490153a5a1b3Sopenharmony_ci
490253a5a1b3Sopenharmony_cistatic void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
490353a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
490453a5a1b3Sopenharmony_ci
490553a5a1b3Sopenharmony_ci    pa_assert(p);
490653a5a1b3Sopenharmony_ci    pa_assert(packet);
490753a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
490853a5a1b3Sopenharmony_ci
490953a5a1b3Sopenharmony_ci    if (pa_pdispatch_run(c->pdispatch, packet, ancil_data, c) < 0) {
491053a5a1b3Sopenharmony_ci        pa_log("invalid packet.");
491153a5a1b3Sopenharmony_ci        native_connection_unlink(c);
491253a5a1b3Sopenharmony_ci    }
491353a5a1b3Sopenharmony_ci}
491453a5a1b3Sopenharmony_ci
491553a5a1b3Sopenharmony_cistatic void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
491653a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
491753a5a1b3Sopenharmony_ci    output_stream *stream;
491853a5a1b3Sopenharmony_ci
491953a5a1b3Sopenharmony_ci    pa_assert(p);
492053a5a1b3Sopenharmony_ci    pa_assert(chunk);
492153a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
492253a5a1b3Sopenharmony_ci
492353a5a1b3Sopenharmony_ci    if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
492453a5a1b3Sopenharmony_ci        pa_log_debug("Client sent block for invalid stream.");
492553a5a1b3Sopenharmony_ci        /* Ignoring */
492653a5a1b3Sopenharmony_ci        return;
492753a5a1b3Sopenharmony_ci    }
492853a5a1b3Sopenharmony_ci
492953a5a1b3Sopenharmony_ci#ifdef PROTOCOL_NATIVE_DEBUG
493053a5a1b3Sopenharmony_ci    pa_log("got %lu bytes from client", (unsigned long) chunk->length);
493153a5a1b3Sopenharmony_ci#endif
493253a5a1b3Sopenharmony_ci
493353a5a1b3Sopenharmony_ci    if (playback_stream_isinstance(stream)) {
493453a5a1b3Sopenharmony_ci        playback_stream *ps = PLAYBACK_STREAM(stream);
493553a5a1b3Sopenharmony_ci
493653a5a1b3Sopenharmony_ci        size_t frame_size = pa_frame_size(&ps->sink_input->sample_spec);
493753a5a1b3Sopenharmony_ci        if (chunk->index % frame_size != 0 || chunk->length % frame_size != 0) {
493853a5a1b3Sopenharmony_ci            pa_log_warn("Client sent non-aligned memblock: index %d, length %d, frame size:"
493953a5a1b3Sopenharmony_ci                "%d", (int) chunk->index, (int) chunk->length, (int) frame_size);
494053a5a1b3Sopenharmony_ci            return;
494153a5a1b3Sopenharmony_ci        }
494253a5a1b3Sopenharmony_ci
494353a5a1b3Sopenharmony_ci        pa_atomic_inc(&ps->seek_or_post_in_queue);
494453a5a1b3Sopenharmony_ci        if (chunk->memblock) {
494553a5a1b3Sopenharmony_ci            if (seek != PA_SEEK_RELATIVE || offset != 0)
494653a5a1b3Sopenharmony_ci                pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset, chunk, NULL);
494753a5a1b3Sopenharmony_ci            else
494853a5a1b3Sopenharmony_ci                pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
494953a5a1b3Sopenharmony_ci        } else
495053a5a1b3Sopenharmony_ci            pa_asyncmsgq_post(ps->sink_input->sink->asyncmsgq, PA_MSGOBJECT(ps->sink_input), SINK_INPUT_MESSAGE_SEEK, PA_UINT_TO_PTR(seek), offset+chunk->length, NULL, NULL);
495153a5a1b3Sopenharmony_ci
495253a5a1b3Sopenharmony_ci    } else {
495353a5a1b3Sopenharmony_ci        upload_stream *u = UPLOAD_STREAM(stream);
495453a5a1b3Sopenharmony_ci        size_t l;
495553a5a1b3Sopenharmony_ci
495653a5a1b3Sopenharmony_ci        if (!u->memchunk.memblock) {
495753a5a1b3Sopenharmony_ci            if (u->length == chunk->length && chunk->memblock) {
495853a5a1b3Sopenharmony_ci                u->memchunk = *chunk;
495953a5a1b3Sopenharmony_ci                pa_memblock_ref(u->memchunk.memblock);
496053a5a1b3Sopenharmony_ci                u->length = 0;
496153a5a1b3Sopenharmony_ci            } else {
496253a5a1b3Sopenharmony_ci                u->memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, u->length);
496353a5a1b3Sopenharmony_ci                u->memchunk.index = u->memchunk.length = 0;
496453a5a1b3Sopenharmony_ci            }
496553a5a1b3Sopenharmony_ci        }
496653a5a1b3Sopenharmony_ci
496753a5a1b3Sopenharmony_ci        pa_assert(u->memchunk.memblock);
496853a5a1b3Sopenharmony_ci
496953a5a1b3Sopenharmony_ci        l = u->length;
497053a5a1b3Sopenharmony_ci        if (l > chunk->length)
497153a5a1b3Sopenharmony_ci            l = chunk->length;
497253a5a1b3Sopenharmony_ci
497353a5a1b3Sopenharmony_ci        if (l > 0) {
497453a5a1b3Sopenharmony_ci            void *dst;
497553a5a1b3Sopenharmony_ci            dst = pa_memblock_acquire(u->memchunk.memblock);
497653a5a1b3Sopenharmony_ci
497753a5a1b3Sopenharmony_ci            if (chunk->memblock) {
497853a5a1b3Sopenharmony_ci                void *src;
497953a5a1b3Sopenharmony_ci                src = pa_memblock_acquire(chunk->memblock);
498053a5a1b3Sopenharmony_ci
498153a5a1b3Sopenharmony_ci                memcpy((uint8_t*) dst + u->memchunk.index + u->memchunk.length,
498253a5a1b3Sopenharmony_ci                       (uint8_t*) src + chunk->index, l);
498353a5a1b3Sopenharmony_ci
498453a5a1b3Sopenharmony_ci                pa_memblock_release(chunk->memblock);
498553a5a1b3Sopenharmony_ci            } else
498653a5a1b3Sopenharmony_ci                pa_silence_memory((uint8_t*) dst + u->memchunk.index + u->memchunk.length, l, &u->sample_spec);
498753a5a1b3Sopenharmony_ci
498853a5a1b3Sopenharmony_ci            pa_memblock_release(u->memchunk.memblock);
498953a5a1b3Sopenharmony_ci
499053a5a1b3Sopenharmony_ci            u->memchunk.length += l;
499153a5a1b3Sopenharmony_ci            u->length -= l;
499253a5a1b3Sopenharmony_ci        }
499353a5a1b3Sopenharmony_ci    }
499453a5a1b3Sopenharmony_ci}
499553a5a1b3Sopenharmony_ci
499653a5a1b3Sopenharmony_cistatic void pstream_die_callback(pa_pstream *p, void *userdata) {
499753a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
499853a5a1b3Sopenharmony_ci
499953a5a1b3Sopenharmony_ci    pa_assert(p);
500053a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
500153a5a1b3Sopenharmony_ci
500253a5a1b3Sopenharmony_ci    native_connection_unlink(c);
500353a5a1b3Sopenharmony_ci    pa_log_info("Connection died.");
500453a5a1b3Sopenharmony_ci}
500553a5a1b3Sopenharmony_ci
500653a5a1b3Sopenharmony_cistatic void pstream_drain_callback(pa_pstream *p, void *userdata) {
500753a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
500853a5a1b3Sopenharmony_ci
500953a5a1b3Sopenharmony_ci    pa_assert(p);
501053a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
501153a5a1b3Sopenharmony_ci
501253a5a1b3Sopenharmony_ci    native_connection_send_memblock(c);
501353a5a1b3Sopenharmony_ci}
501453a5a1b3Sopenharmony_ci
501553a5a1b3Sopenharmony_cistatic void pstream_revoke_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
501653a5a1b3Sopenharmony_ci    pa_thread_mq *q;
501753a5a1b3Sopenharmony_ci
501853a5a1b3Sopenharmony_ci    if (!(q = pa_thread_mq_get()))
501953a5a1b3Sopenharmony_ci        pa_pstream_send_revoke(p, block_id);
502053a5a1b3Sopenharmony_ci    else
502153a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_REVOKE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
502253a5a1b3Sopenharmony_ci}
502353a5a1b3Sopenharmony_ci
502453a5a1b3Sopenharmony_cistatic void pstream_release_callback(pa_pstream *p, uint32_t block_id, void *userdata) {
502553a5a1b3Sopenharmony_ci    pa_thread_mq *q;
502653a5a1b3Sopenharmony_ci
502753a5a1b3Sopenharmony_ci    if (!(q = pa_thread_mq_get()))
502853a5a1b3Sopenharmony_ci        pa_pstream_send_release(p, block_id);
502953a5a1b3Sopenharmony_ci    else
503053a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(q->outq, PA_MSGOBJECT(userdata), CONNECTION_MESSAGE_RELEASE, PA_UINT_TO_PTR(block_id), 0, NULL, NULL);
503153a5a1b3Sopenharmony_ci}
503253a5a1b3Sopenharmony_ci
503353a5a1b3Sopenharmony_ci/*** client callbacks ***/
503453a5a1b3Sopenharmony_ci
503553a5a1b3Sopenharmony_cistatic void client_kill_cb(pa_client *c) {
503653a5a1b3Sopenharmony_ci    pa_assert(c);
503753a5a1b3Sopenharmony_ci
503853a5a1b3Sopenharmony_ci    native_connection_unlink(PA_NATIVE_CONNECTION(c->userdata));
503953a5a1b3Sopenharmony_ci    pa_log_info("Connection killed.");
504053a5a1b3Sopenharmony_ci}
504153a5a1b3Sopenharmony_ci
504253a5a1b3Sopenharmony_cistatic void client_send_event_cb(pa_client *client, const char*event, pa_proplist *pl) {
504353a5a1b3Sopenharmony_ci    pa_tagstruct *t;
504453a5a1b3Sopenharmony_ci    pa_native_connection *c;
504553a5a1b3Sopenharmony_ci
504653a5a1b3Sopenharmony_ci    pa_assert(client);
504753a5a1b3Sopenharmony_ci    c = PA_NATIVE_CONNECTION(client->userdata);
504853a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
504953a5a1b3Sopenharmony_ci
505053a5a1b3Sopenharmony_ci    if (c->version < 15)
505153a5a1b3Sopenharmony_ci      return;
505253a5a1b3Sopenharmony_ci
505353a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
505453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_CLIENT_EVENT);
505553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, (uint32_t) -1); /* tag */
505653a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, event);
505753a5a1b3Sopenharmony_ci    pa_tagstruct_put_proplist(t, pl);
505853a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(c->pstream, t);
505953a5a1b3Sopenharmony_ci}
506053a5a1b3Sopenharmony_ci
506153a5a1b3Sopenharmony_ci/*** module entry points ***/
506253a5a1b3Sopenharmony_ci
506353a5a1b3Sopenharmony_cistatic void auth_timeout(pa_mainloop_api*m, pa_time_event *e, const struct timeval *t, void *userdata) {
506453a5a1b3Sopenharmony_ci    pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
506553a5a1b3Sopenharmony_ci
506653a5a1b3Sopenharmony_ci    pa_assert(m);
506753a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
506853a5a1b3Sopenharmony_ci    pa_assert(c->auth_timeout_event == e);
506953a5a1b3Sopenharmony_ci
507053a5a1b3Sopenharmony_ci    if (!c->authorized) {
507153a5a1b3Sopenharmony_ci        native_connection_unlink(c);
507253a5a1b3Sopenharmony_ci        pa_log_info("Connection terminated due to authentication timeout.");
507353a5a1b3Sopenharmony_ci    }
507453a5a1b3Sopenharmony_ci}
507553a5a1b3Sopenharmony_ci
507653a5a1b3Sopenharmony_civoid pa_native_protocol_connect(pa_native_protocol *p, pa_iochannel *io, pa_native_options *o) {
507753a5a1b3Sopenharmony_ci    pa_native_connection *c;
507853a5a1b3Sopenharmony_ci    char pname[128];
507953a5a1b3Sopenharmony_ci    pa_client *client;
508053a5a1b3Sopenharmony_ci    pa_client_new_data data;
508153a5a1b3Sopenharmony_ci
508253a5a1b3Sopenharmony_ci    pa_assert(p);
508353a5a1b3Sopenharmony_ci    pa_assert(io);
508453a5a1b3Sopenharmony_ci    pa_assert(o);
508553a5a1b3Sopenharmony_ci
508653a5a1b3Sopenharmony_ci    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
508753a5a1b3Sopenharmony_ci        pa_log_warn("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
508853a5a1b3Sopenharmony_ci        pa_iochannel_free(io);
508953a5a1b3Sopenharmony_ci        return;
509053a5a1b3Sopenharmony_ci    }
509153a5a1b3Sopenharmony_ci
509253a5a1b3Sopenharmony_ci    pa_client_new_data_init(&data);
509353a5a1b3Sopenharmony_ci    data.module = o->module;
509453a5a1b3Sopenharmony_ci    data.driver = __FILE__;
509553a5a1b3Sopenharmony_ci    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
509653a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "Native client (%s)", pname);
509753a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "native-protocol.peer", pname);
509853a5a1b3Sopenharmony_ci    client = pa_client_new(p->core, &data);
509953a5a1b3Sopenharmony_ci    pa_client_new_data_done(&data);
510053a5a1b3Sopenharmony_ci
510153a5a1b3Sopenharmony_ci    if (!client)
510253a5a1b3Sopenharmony_ci        return;
510353a5a1b3Sopenharmony_ci
510453a5a1b3Sopenharmony_ci    c = pa_msgobject_new(pa_native_connection);
510553a5a1b3Sopenharmony_ci    c->parent.parent.free = native_connection_free;
510653a5a1b3Sopenharmony_ci    c->parent.process_msg = native_connection_process_msg;
510753a5a1b3Sopenharmony_ci    c->protocol = p;
510853a5a1b3Sopenharmony_ci    c->options = pa_native_options_ref(o);
510953a5a1b3Sopenharmony_ci    c->authorized = false;
511053a5a1b3Sopenharmony_ci    c->srbpending = NULL;
511153a5a1b3Sopenharmony_ci
511253a5a1b3Sopenharmony_ci    if (o->auth_anonymous) {
511353a5a1b3Sopenharmony_ci        pa_log_info("Client authenticated anonymously.");
511453a5a1b3Sopenharmony_ci        c->authorized = true;
511553a5a1b3Sopenharmony_ci    }
511653a5a1b3Sopenharmony_ci
511753a5a1b3Sopenharmony_ci    if (!c->authorized &&
511853a5a1b3Sopenharmony_ci        o->auth_ip_acl &&
511953a5a1b3Sopenharmony_ci        pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
512053a5a1b3Sopenharmony_ci
512153a5a1b3Sopenharmony_ci        pa_log_info("Client authenticated by IP ACL.");
512253a5a1b3Sopenharmony_ci        c->authorized = true;
512353a5a1b3Sopenharmony_ci    }
512453a5a1b3Sopenharmony_ci
512553a5a1b3Sopenharmony_ci    if (!c->authorized)
512653a5a1b3Sopenharmony_ci        c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
512753a5a1b3Sopenharmony_ci    else
512853a5a1b3Sopenharmony_ci        c->auth_timeout_event = NULL;
512953a5a1b3Sopenharmony_ci
513053a5a1b3Sopenharmony_ci    c->is_local = pa_iochannel_socket_is_local(io);
513153a5a1b3Sopenharmony_ci    c->version = 8;
513253a5a1b3Sopenharmony_ci
513353a5a1b3Sopenharmony_ci    c->client = client;
513453a5a1b3Sopenharmony_ci    c->client->kill = client_kill_cb;
513553a5a1b3Sopenharmony_ci    c->client->send_event = client_send_event_cb;
513653a5a1b3Sopenharmony_ci    c->client->userdata = c;
513753a5a1b3Sopenharmony_ci
513853a5a1b3Sopenharmony_ci    c->rw_mempool = NULL;
513953a5a1b3Sopenharmony_ci
514053a5a1b3Sopenharmony_ci    c->pstream = pa_pstream_new(p->core->mainloop, io, p->core->mempool);
514153a5a1b3Sopenharmony_ci    pa_pstream_set_receive_packet_callback(c->pstream, pstream_packet_callback, c);
514253a5a1b3Sopenharmony_ci    pa_pstream_set_receive_memblock_callback(c->pstream, pstream_memblock_callback, c);
514353a5a1b3Sopenharmony_ci    pa_pstream_set_die_callback(c->pstream, pstream_die_callback, c);
514453a5a1b3Sopenharmony_ci    pa_pstream_set_drain_callback(c->pstream, pstream_drain_callback, c);
514553a5a1b3Sopenharmony_ci    pa_pstream_set_revoke_callback(c->pstream, pstream_revoke_callback, c);
514653a5a1b3Sopenharmony_ci    pa_pstream_set_release_callback(c->pstream, pstream_release_callback, c);
514753a5a1b3Sopenharmony_ci
514853a5a1b3Sopenharmony_ci    c->pdispatch = pa_pdispatch_new(p->core->mainloop, true, command_table, PA_COMMAND_MAX);
514953a5a1b3Sopenharmony_ci
515053a5a1b3Sopenharmony_ci    c->record_streams = pa_idxset_new(NULL, NULL);
515153a5a1b3Sopenharmony_ci    c->output_streams = pa_idxset_new(NULL, NULL);
515253a5a1b3Sopenharmony_ci
515353a5a1b3Sopenharmony_ci    c->rrobin_index = PA_IDXSET_INVALID;
515453a5a1b3Sopenharmony_ci    c->subscription = NULL;
515553a5a1b3Sopenharmony_ci
515653a5a1b3Sopenharmony_ci    pa_idxset_put(p->connections, c, NULL);
515753a5a1b3Sopenharmony_ci
515853a5a1b3Sopenharmony_ci#ifdef HAVE_CREDS
515953a5a1b3Sopenharmony_ci    if (pa_iochannel_creds_supported(io))
516053a5a1b3Sopenharmony_ci        pa_iochannel_creds_enable(io);
516153a5a1b3Sopenharmony_ci#endif
516253a5a1b3Sopenharmony_ci
516353a5a1b3Sopenharmony_ci    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_CONNECTION_PUT], c);
516453a5a1b3Sopenharmony_ci}
516553a5a1b3Sopenharmony_ci
516653a5a1b3Sopenharmony_civoid pa_native_protocol_disconnect(pa_native_protocol *p, pa_module *m) {
516753a5a1b3Sopenharmony_ci    pa_native_connection *c;
516853a5a1b3Sopenharmony_ci    void *state = NULL;
516953a5a1b3Sopenharmony_ci
517053a5a1b3Sopenharmony_ci    pa_assert(p);
517153a5a1b3Sopenharmony_ci    pa_assert(m);
517253a5a1b3Sopenharmony_ci
517353a5a1b3Sopenharmony_ci    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
517453a5a1b3Sopenharmony_ci        if (c->options->module == m)
517553a5a1b3Sopenharmony_ci            native_connection_unlink(c);
517653a5a1b3Sopenharmony_ci}
517753a5a1b3Sopenharmony_ci
517853a5a1b3Sopenharmony_cistatic pa_native_protocol* native_protocol_new(pa_core *c) {
517953a5a1b3Sopenharmony_ci    pa_native_protocol *p;
518053a5a1b3Sopenharmony_ci    pa_native_hook_t h;
518153a5a1b3Sopenharmony_ci
518253a5a1b3Sopenharmony_ci    pa_assert(c);
518353a5a1b3Sopenharmony_ci
518453a5a1b3Sopenharmony_ci    p = pa_xnew(pa_native_protocol, 1);
518553a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(p);
518653a5a1b3Sopenharmony_ci    p->core = c;
518753a5a1b3Sopenharmony_ci    p->connections = pa_idxset_new(NULL, NULL);
518853a5a1b3Sopenharmony_ci
518953a5a1b3Sopenharmony_ci    p->servers = NULL;
519053a5a1b3Sopenharmony_ci
519153a5a1b3Sopenharmony_ci    p->extensions = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
519253a5a1b3Sopenharmony_ci
519353a5a1b3Sopenharmony_ci    for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
519453a5a1b3Sopenharmony_ci        pa_hook_init(&p->hooks[h], p);
519553a5a1b3Sopenharmony_ci
519653a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_set(c, "native-protocol", p) >= 0);
519753a5a1b3Sopenharmony_ci
519853a5a1b3Sopenharmony_ci    return p;
519953a5a1b3Sopenharmony_ci}
520053a5a1b3Sopenharmony_ci
520153a5a1b3Sopenharmony_cipa_native_protocol* pa_native_protocol_get(pa_core *c) {
520253a5a1b3Sopenharmony_ci    pa_native_protocol *p;
520353a5a1b3Sopenharmony_ci
520453a5a1b3Sopenharmony_ci    if ((p = pa_shared_get(c, "native-protocol")))
520553a5a1b3Sopenharmony_ci        return pa_native_protocol_ref(p);
520653a5a1b3Sopenharmony_ci
520753a5a1b3Sopenharmony_ci    return native_protocol_new(c);
520853a5a1b3Sopenharmony_ci}
520953a5a1b3Sopenharmony_ci
521053a5a1b3Sopenharmony_cipa_native_protocol* pa_native_protocol_ref(pa_native_protocol *p) {
521153a5a1b3Sopenharmony_ci    pa_assert(p);
521253a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
521353a5a1b3Sopenharmony_ci
521453a5a1b3Sopenharmony_ci    PA_REFCNT_INC(p);
521553a5a1b3Sopenharmony_ci
521653a5a1b3Sopenharmony_ci    return p;
521753a5a1b3Sopenharmony_ci}
521853a5a1b3Sopenharmony_ci
521953a5a1b3Sopenharmony_civoid pa_native_protocol_unref(pa_native_protocol *p) {
522053a5a1b3Sopenharmony_ci    pa_native_connection *c;
522153a5a1b3Sopenharmony_ci    pa_native_hook_t h;
522253a5a1b3Sopenharmony_ci
522353a5a1b3Sopenharmony_ci    pa_assert(p);
522453a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
522553a5a1b3Sopenharmony_ci
522653a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(p) > 0)
522753a5a1b3Sopenharmony_ci        return;
522853a5a1b3Sopenharmony_ci
522953a5a1b3Sopenharmony_ci    while ((c = pa_idxset_first(p->connections, NULL)))
523053a5a1b3Sopenharmony_ci        native_connection_unlink(c);
523153a5a1b3Sopenharmony_ci
523253a5a1b3Sopenharmony_ci    pa_idxset_free(p->connections, NULL);
523353a5a1b3Sopenharmony_ci
523453a5a1b3Sopenharmony_ci    pa_strlist_free(p->servers);
523553a5a1b3Sopenharmony_ci
523653a5a1b3Sopenharmony_ci    for (h = 0; h < PA_NATIVE_HOOK_MAX; h++)
523753a5a1b3Sopenharmony_ci        pa_hook_done(&p->hooks[h]);
523853a5a1b3Sopenharmony_ci
523953a5a1b3Sopenharmony_ci    pa_hashmap_free(p->extensions);
524053a5a1b3Sopenharmony_ci
524153a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_remove(p->core, "native-protocol") >= 0);
524253a5a1b3Sopenharmony_ci
524353a5a1b3Sopenharmony_ci    pa_xfree(p);
524453a5a1b3Sopenharmony_ci}
524553a5a1b3Sopenharmony_ci
524653a5a1b3Sopenharmony_civoid pa_native_protocol_add_server_string(pa_native_protocol *p, const char *name) {
524753a5a1b3Sopenharmony_ci    pa_assert(p);
524853a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
524953a5a1b3Sopenharmony_ci    pa_assert(name);
525053a5a1b3Sopenharmony_ci
525153a5a1b3Sopenharmony_ci    p->servers = pa_strlist_prepend(p->servers, name);
525253a5a1b3Sopenharmony_ci
525353a5a1b3Sopenharmony_ci    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
525453a5a1b3Sopenharmony_ci}
525553a5a1b3Sopenharmony_ci
525653a5a1b3Sopenharmony_civoid pa_native_protocol_remove_server_string(pa_native_protocol *p, const char *name) {
525753a5a1b3Sopenharmony_ci    pa_assert(p);
525853a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
525953a5a1b3Sopenharmony_ci    pa_assert(name);
526053a5a1b3Sopenharmony_ci
526153a5a1b3Sopenharmony_ci    p->servers = pa_strlist_remove(p->servers, name);
526253a5a1b3Sopenharmony_ci
526353a5a1b3Sopenharmony_ci    pa_hook_fire(&p->hooks[PA_NATIVE_HOOK_SERVERS_CHANGED], p->servers);
526453a5a1b3Sopenharmony_ci}
526553a5a1b3Sopenharmony_ci
526653a5a1b3Sopenharmony_cipa_hook *pa_native_protocol_hooks(pa_native_protocol *p) {
526753a5a1b3Sopenharmony_ci    pa_assert(p);
526853a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
526953a5a1b3Sopenharmony_ci
527053a5a1b3Sopenharmony_ci    return p->hooks;
527153a5a1b3Sopenharmony_ci}
527253a5a1b3Sopenharmony_ci
527353a5a1b3Sopenharmony_cipa_strlist *pa_native_protocol_servers(pa_native_protocol *p) {
527453a5a1b3Sopenharmony_ci    pa_assert(p);
527553a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
527653a5a1b3Sopenharmony_ci
527753a5a1b3Sopenharmony_ci    return p->servers;
527853a5a1b3Sopenharmony_ci}
527953a5a1b3Sopenharmony_ci
528053a5a1b3Sopenharmony_ciint pa_native_protocol_install_ext(pa_native_protocol *p, pa_module *m, pa_native_protocol_ext_cb_t cb) {
528153a5a1b3Sopenharmony_ci    pa_assert(p);
528253a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
528353a5a1b3Sopenharmony_ci    pa_assert(m);
528453a5a1b3Sopenharmony_ci    pa_assert(cb);
528553a5a1b3Sopenharmony_ci    pa_assert(!pa_hashmap_get(p->extensions, m));
528653a5a1b3Sopenharmony_ci
528753a5a1b3Sopenharmony_ci    pa_assert_se(pa_hashmap_put(p->extensions, m, cb) == 0);
528853a5a1b3Sopenharmony_ci    return 0;
528953a5a1b3Sopenharmony_ci}
529053a5a1b3Sopenharmony_ci
529153a5a1b3Sopenharmony_civoid pa_native_protocol_remove_ext(pa_native_protocol *p, pa_module *m) {
529253a5a1b3Sopenharmony_ci    pa_assert(p);
529353a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
529453a5a1b3Sopenharmony_ci    pa_assert(m);
529553a5a1b3Sopenharmony_ci
529653a5a1b3Sopenharmony_ci    pa_assert_se(pa_hashmap_remove(p->extensions, m));
529753a5a1b3Sopenharmony_ci}
529853a5a1b3Sopenharmony_ci
529953a5a1b3Sopenharmony_cipa_native_options* pa_native_options_new(void) {
530053a5a1b3Sopenharmony_ci    pa_native_options *o;
530153a5a1b3Sopenharmony_ci
530253a5a1b3Sopenharmony_ci    o = pa_xnew0(pa_native_options, 1);
530353a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(o);
530453a5a1b3Sopenharmony_ci
530553a5a1b3Sopenharmony_ci    return o;
530653a5a1b3Sopenharmony_ci}
530753a5a1b3Sopenharmony_ci
530853a5a1b3Sopenharmony_cipa_native_options* pa_native_options_ref(pa_native_options *o) {
530953a5a1b3Sopenharmony_ci    pa_assert(o);
531053a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(o) >= 1);
531153a5a1b3Sopenharmony_ci
531253a5a1b3Sopenharmony_ci    PA_REFCNT_INC(o);
531353a5a1b3Sopenharmony_ci
531453a5a1b3Sopenharmony_ci    return o;
531553a5a1b3Sopenharmony_ci}
531653a5a1b3Sopenharmony_ci
531753a5a1b3Sopenharmony_civoid pa_native_options_unref(pa_native_options *o) {
531853a5a1b3Sopenharmony_ci    pa_assert(o);
531953a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(o) >= 1);
532053a5a1b3Sopenharmony_ci
532153a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(o) > 0)
532253a5a1b3Sopenharmony_ci        return;
532353a5a1b3Sopenharmony_ci
532453a5a1b3Sopenharmony_ci    pa_xfree(o->auth_group);
532553a5a1b3Sopenharmony_ci
532653a5a1b3Sopenharmony_ci    if (o->auth_ip_acl)
532753a5a1b3Sopenharmony_ci        pa_ip_acl_free(o->auth_ip_acl);
532853a5a1b3Sopenharmony_ci
532953a5a1b3Sopenharmony_ci    if (o->auth_cookie)
533053a5a1b3Sopenharmony_ci        pa_auth_cookie_unref(o->auth_cookie);
533153a5a1b3Sopenharmony_ci
533253a5a1b3Sopenharmony_ci    pa_xfree(o);
533353a5a1b3Sopenharmony_ci}
533453a5a1b3Sopenharmony_ci
533553a5a1b3Sopenharmony_ciint pa_native_options_parse(pa_native_options *o, pa_core *c, pa_modargs *ma) {
533653a5a1b3Sopenharmony_ci    bool enabled;
533753a5a1b3Sopenharmony_ci    const char *acl;
533853a5a1b3Sopenharmony_ci
533953a5a1b3Sopenharmony_ci    pa_assert(o);
534053a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(o) >= 1);
534153a5a1b3Sopenharmony_ci    pa_assert(ma);
534253a5a1b3Sopenharmony_ci
534353a5a1b3Sopenharmony_ci    o->srbchannel = true;
534453a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "srbchannel", &o->srbchannel) < 0) {
534553a5a1b3Sopenharmony_ci        pa_log_error("srbchannel= expects a boolean argument.");
534653a5a1b3Sopenharmony_ci        return -1;
534753a5a1b3Sopenharmony_ci    }
534853a5a1b3Sopenharmony_ci
534953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
535053a5a1b3Sopenharmony_ci        pa_log_error("auth-anonymous= expects a boolean argument.");
535153a5a1b3Sopenharmony_ci        return -1;
535253a5a1b3Sopenharmony_ci    }
535353a5a1b3Sopenharmony_ci
535453a5a1b3Sopenharmony_ci    enabled = true;
535553a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "auth-group-enable", &enabled) < 0) {
535653a5a1b3Sopenharmony_ci        pa_log_error("auth-group-enable= expects a boolean argument.");
535753a5a1b3Sopenharmony_ci        return -1;
535853a5a1b3Sopenharmony_ci    }
535953a5a1b3Sopenharmony_ci
536053a5a1b3Sopenharmony_ci    pa_xfree(o->auth_group);
536153a5a1b3Sopenharmony_ci    o->auth_group = enabled ? pa_xstrdup(pa_modargs_get_value(ma, "auth-group", pa_in_system_mode() ? PA_ACCESS_GROUP : NULL)) : NULL;
536253a5a1b3Sopenharmony_ci
536353a5a1b3Sopenharmony_ci#ifndef HAVE_CREDS
536453a5a1b3Sopenharmony_ci    if (o->auth_group)
536553a5a1b3Sopenharmony_ci        pa_log_error("Authentication group configured, but not available on local system. Ignoring.");
536653a5a1b3Sopenharmony_ci#endif
536753a5a1b3Sopenharmony_ci
536853a5a1b3Sopenharmony_ci    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
536953a5a1b3Sopenharmony_ci        pa_ip_acl *ipa;
537053a5a1b3Sopenharmony_ci
537153a5a1b3Sopenharmony_ci        if (!(ipa = pa_ip_acl_new(acl))) {
537253a5a1b3Sopenharmony_ci            pa_log_error("Failed to parse IP ACL '%s'", acl);
537353a5a1b3Sopenharmony_ci            return -1;
537453a5a1b3Sopenharmony_ci        }
537553a5a1b3Sopenharmony_ci
537653a5a1b3Sopenharmony_ci        if (o->auth_ip_acl)
537753a5a1b3Sopenharmony_ci            pa_ip_acl_free(o->auth_ip_acl);
537853a5a1b3Sopenharmony_ci
537953a5a1b3Sopenharmony_ci        o->auth_ip_acl = ipa;
538053a5a1b3Sopenharmony_ci    }
538153a5a1b3Sopenharmony_ci
538253a5a1b3Sopenharmony_ci    enabled = true;
538353a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
538453a5a1b3Sopenharmony_ci        pa_log_error("auth-cookie-enabled= expects a boolean argument.");
538553a5a1b3Sopenharmony_ci        return -1;
538653a5a1b3Sopenharmony_ci    }
538753a5a1b3Sopenharmony_ci
538853a5a1b3Sopenharmony_ci    if (o->auth_cookie)
538953a5a1b3Sopenharmony_ci        pa_auth_cookie_unref(o->auth_cookie);
539053a5a1b3Sopenharmony_ci
539153a5a1b3Sopenharmony_ci    if (enabled) {
539253a5a1b3Sopenharmony_ci        const char *cn;
539353a5a1b3Sopenharmony_ci
539453a5a1b3Sopenharmony_ci        /* The new name for this is 'auth-cookie', for compat reasons
539553a5a1b3Sopenharmony_ci         * we check the old name too */
539653a5a1b3Sopenharmony_ci        cn = pa_modargs_get_value(ma, "auth-cookie", NULL);
539753a5a1b3Sopenharmony_ci        if (!cn)
539853a5a1b3Sopenharmony_ci            cn = pa_modargs_get_value(ma, "cookie", NULL);
539953a5a1b3Sopenharmony_ci
540053a5a1b3Sopenharmony_ci        if (cn)
540153a5a1b3Sopenharmony_ci            o->auth_cookie = pa_auth_cookie_get(c, cn, true, PA_NATIVE_COOKIE_LENGTH);
540253a5a1b3Sopenharmony_ci        else {
540353a5a1b3Sopenharmony_ci            o->auth_cookie = pa_auth_cookie_get(c, PA_NATIVE_COOKIE_FILE, false, PA_NATIVE_COOKIE_LENGTH);
540453a5a1b3Sopenharmony_ci            if (!o->auth_cookie) {
540553a5a1b3Sopenharmony_ci                char *fallback_path;
540653a5a1b3Sopenharmony_ci
540753a5a1b3Sopenharmony_ci                if (pa_append_to_home_dir(PA_NATIVE_COOKIE_FILE_FALLBACK, &fallback_path) >= 0) {
540853a5a1b3Sopenharmony_ci                    o->auth_cookie = pa_auth_cookie_get(c, fallback_path, false, PA_NATIVE_COOKIE_LENGTH);
540953a5a1b3Sopenharmony_ci                    pa_xfree(fallback_path);
541053a5a1b3Sopenharmony_ci                }
541153a5a1b3Sopenharmony_ci
541253a5a1b3Sopenharmony_ci                if (!o->auth_cookie)
541353a5a1b3Sopenharmony_ci                    o->auth_cookie = pa_auth_cookie_get(c, PA_NATIVE_COOKIE_FILE, true, PA_NATIVE_COOKIE_LENGTH);
541453a5a1b3Sopenharmony_ci            }
541553a5a1b3Sopenharmony_ci        }
541653a5a1b3Sopenharmony_ci
541753a5a1b3Sopenharmony_ci        if (!o->auth_cookie)
541853a5a1b3Sopenharmony_ci            return -1;
541953a5a1b3Sopenharmony_ci
542053a5a1b3Sopenharmony_ci    } else
542153a5a1b3Sopenharmony_ci          o->auth_cookie = NULL;
542253a5a1b3Sopenharmony_ci
542353a5a1b3Sopenharmony_ci    return 0;
542453a5a1b3Sopenharmony_ci}
542553a5a1b3Sopenharmony_ci
542653a5a1b3Sopenharmony_cipa_pstream* pa_native_connection_get_pstream(pa_native_connection *c) {
542753a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
542853a5a1b3Sopenharmony_ci
542953a5a1b3Sopenharmony_ci    return c->pstream;
543053a5a1b3Sopenharmony_ci}
543153a5a1b3Sopenharmony_ci
543253a5a1b3Sopenharmony_cipa_client* pa_native_connection_get_client(pa_native_connection *c) {
543353a5a1b3Sopenharmony_ci    pa_native_connection_assert_ref(c);
543453a5a1b3Sopenharmony_ci
543553a5a1b3Sopenharmony_ci    return c->client;
543653a5a1b3Sopenharmony_ci}
5437