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#include <errno.h>
2653a5a1b3Sopenharmony_ci#include <string.h>
2753a5a1b3Sopenharmony_ci#include <stdio.h>
2853a5a1b3Sopenharmony_ci#include <stdlib.h>
2953a5a1b3Sopenharmony_ci
3053a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3153a5a1b3Sopenharmony_ci#include <pulse/sample.h>
3253a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
3353a5a1b3Sopenharmony_ci#include <pulse/utf8.h>
3453a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
3553a5a1b3Sopenharmony_ci#include <pulse/proplist.h>
3653a5a1b3Sopenharmony_ci
3753a5a1b3Sopenharmony_ci#include <pulsecore/esound.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/memblock.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/client.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/sink-input.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/sink.h>
4253a5a1b3Sopenharmony_ci#include <pulsecore/source-output.h>
4353a5a1b3Sopenharmony_ci#include <pulsecore/source.h>
4453a5a1b3Sopenharmony_ci#include <pulsecore/core-scache.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
4653a5a1b3Sopenharmony_ci#include <pulsecore/namereg.h>
4753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4853a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4953a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
5053a5a1b3Sopenharmony_ci#include <pulsecore/ipacl.h>
5153a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
5253a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
5353a5a1b3Sopenharmony_ci#include <pulsecore/shared.h>
5453a5a1b3Sopenharmony_ci#include <pulsecore/endianmacros.h>
5553a5a1b3Sopenharmony_ci
5653a5a1b3Sopenharmony_ci#include "protocol-esound.h"
5753a5a1b3Sopenharmony_ci
5853a5a1b3Sopenharmony_ci/* Don't accept more connection than this */
5953a5a1b3Sopenharmony_ci#define MAX_CONNECTIONS 64
6053a5a1b3Sopenharmony_ci
6153a5a1b3Sopenharmony_ci/* Kick a client if it doesn't authenticate within this time */
6253a5a1b3Sopenharmony_ci#define AUTH_TIMEOUT (5*PA_USEC_PER_SEC)
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ci#define DEFAULT_COOKIE_FILE ".esd_auth"
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_ci#define PLAYBACK_BUFFER_SECONDS (.25)
6753a5a1b3Sopenharmony_ci#define PLAYBACK_BUFFER_FRAGMENTS (10)
6853a5a1b3Sopenharmony_ci#define RECORD_BUFFER_SECONDS (5)
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci#define MAX_CACHE_SAMPLE_SIZE (2048000)
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_ci#define DEFAULT_SINK_LATENCY (150*PA_USEC_PER_MSEC)
7353a5a1b3Sopenharmony_ci#define DEFAULT_SOURCE_LATENCY (150*PA_USEC_PER_MSEC)
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci#define SCACHE_PREFIX "esound."
7653a5a1b3Sopenharmony_ci
7753a5a1b3Sopenharmony_ci/* This is heavily based on esound's code */
7853a5a1b3Sopenharmony_ci
7953a5a1b3Sopenharmony_citypedef struct connection {
8053a5a1b3Sopenharmony_ci    pa_msgobject parent;
8153a5a1b3Sopenharmony_ci
8253a5a1b3Sopenharmony_ci    uint32_t index;
8353a5a1b3Sopenharmony_ci    bool dead;
8453a5a1b3Sopenharmony_ci    pa_esound_protocol *protocol;
8553a5a1b3Sopenharmony_ci    pa_esound_options *options;
8653a5a1b3Sopenharmony_ci    pa_iochannel *io;
8753a5a1b3Sopenharmony_ci    pa_client *client;
8853a5a1b3Sopenharmony_ci    bool authorized, swap_byte_order;
8953a5a1b3Sopenharmony_ci    void *write_data;
9053a5a1b3Sopenharmony_ci    size_t write_data_alloc, write_data_index, write_data_length;
9153a5a1b3Sopenharmony_ci    void *read_data;
9253a5a1b3Sopenharmony_ci    size_t read_data_alloc, read_data_length;
9353a5a1b3Sopenharmony_ci    esd_proto_t request;
9453a5a1b3Sopenharmony_ci    esd_client_state_t state;
9553a5a1b3Sopenharmony_ci    pa_sink_input *sink_input;
9653a5a1b3Sopenharmony_ci    pa_source_output *source_output;
9753a5a1b3Sopenharmony_ci    pa_memblockq *input_memblockq, *output_memblockq;
9853a5a1b3Sopenharmony_ci    pa_defer_event *defer_event;
9953a5a1b3Sopenharmony_ci
10053a5a1b3Sopenharmony_ci    char *original_name;
10153a5a1b3Sopenharmony_ci
10253a5a1b3Sopenharmony_ci    struct {
10353a5a1b3Sopenharmony_ci        pa_memblock *current_memblock;
10453a5a1b3Sopenharmony_ci        size_t memblock_index;
10553a5a1b3Sopenharmony_ci        pa_atomic_t missing;
10653a5a1b3Sopenharmony_ci        bool underrun;
10753a5a1b3Sopenharmony_ci    } playback;
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci    struct {
11053a5a1b3Sopenharmony_ci        pa_memchunk memchunk;
11153a5a1b3Sopenharmony_ci        char *name;
11253a5a1b3Sopenharmony_ci        pa_sample_spec sample_spec;
11353a5a1b3Sopenharmony_ci    } scache;
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    pa_time_event *auth_timeout_event;
11653a5a1b3Sopenharmony_ci} connection;
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ciPA_DEFINE_PRIVATE_CLASS(connection, pa_msgobject);
11953a5a1b3Sopenharmony_ci#define CONNECTION(o) (connection_cast(o))
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_cistruct pa_esound_protocol {
12253a5a1b3Sopenharmony_ci    PA_REFCNT_DECLARE;
12353a5a1b3Sopenharmony_ci
12453a5a1b3Sopenharmony_ci    pa_core *core;
12553a5a1b3Sopenharmony_ci    pa_idxset *connections;
12653a5a1b3Sopenharmony_ci    unsigned n_player;
12753a5a1b3Sopenharmony_ci};
12853a5a1b3Sopenharmony_ci
12953a5a1b3Sopenharmony_cienum {
13053a5a1b3Sopenharmony_ci    SINK_INPUT_MESSAGE_POST_DATA = PA_SINK_INPUT_MESSAGE_MAX, /* data from main loop to sink input */
13153a5a1b3Sopenharmony_ci    SINK_INPUT_MESSAGE_DISABLE_PREBUF
13253a5a1b3Sopenharmony_ci};
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_cienum {
13553a5a1b3Sopenharmony_ci    CONNECTION_MESSAGE_REQUEST_DATA,
13653a5a1b3Sopenharmony_ci    CONNECTION_MESSAGE_POST_DATA,
13753a5a1b3Sopenharmony_ci    CONNECTION_MESSAGE_UNLINK_CONNECTION
13853a5a1b3Sopenharmony_ci};
13953a5a1b3Sopenharmony_ci
14053a5a1b3Sopenharmony_citypedef struct proto_handler {
14153a5a1b3Sopenharmony_ci    size_t data_length;
14253a5a1b3Sopenharmony_ci    int (*proc)(connection *c, esd_proto_t request, const void *data, size_t length);
14353a5a1b3Sopenharmony_ci    const char *description;
14453a5a1b3Sopenharmony_ci} esd_proto_handler_info_t;
14553a5a1b3Sopenharmony_ci
14653a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk);
14753a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes);
14853a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes);
14953a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i);
15053a5a1b3Sopenharmony_cistatic int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk);
15153a5a1b3Sopenharmony_cistatic pa_usec_t source_output_get_latency_cb(pa_source_output *o);
15253a5a1b3Sopenharmony_ci
15353a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk);
15453a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o);
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_cistatic int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length);
15753a5a1b3Sopenharmony_cistatic int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length);
15853a5a1b3Sopenharmony_cistatic int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length);
15953a5a1b3Sopenharmony_cistatic int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length);
16053a5a1b3Sopenharmony_cistatic int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length);
16153a5a1b3Sopenharmony_cistatic int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length);
16253a5a1b3Sopenharmony_cistatic int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length);
16353a5a1b3Sopenharmony_cistatic int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *data, size_t length);
16453a5a1b3Sopenharmony_cistatic int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length);
16553a5a1b3Sopenharmony_cistatic int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length);
16653a5a1b3Sopenharmony_cistatic int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length);
16753a5a1b3Sopenharmony_cistatic int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length);
16853a5a1b3Sopenharmony_cistatic int esd_proto_standby_mode(connection *c, esd_proto_t request, const void *data, size_t length);
16953a5a1b3Sopenharmony_ci
17053a5a1b3Sopenharmony_ci/* the big map of protocol handler info */
17153a5a1b3Sopenharmony_cistatic struct proto_handler proto_map[ESD_PROTO_MAX] = {
17253a5a1b3Sopenharmony_ci    { ESD_KEY_LEN + sizeof(int),      esd_proto_connect, "connect" },
17353a5a1b3Sopenharmony_ci    { ESD_KEY_LEN + sizeof(int),      NULL, "lock" },
17453a5a1b3Sopenharmony_ci    { ESD_KEY_LEN + sizeof(int),      NULL, "unlock" },
17553a5a1b3Sopenharmony_ci
17653a5a1b3Sopenharmony_ci    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_play, "stream play" },
17753a5a1b3Sopenharmony_ci    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream rec" },
17853a5a1b3Sopenharmony_ci    { ESD_NAME_MAX + 2 * sizeof(int), esd_proto_stream_record, "stream mon" },
17953a5a1b3Sopenharmony_ci
18053a5a1b3Sopenharmony_ci    { ESD_NAME_MAX + 3 * sizeof(int), esd_proto_sample_cache, "sample cache" },                      /* 6 */
18153a5a1b3Sopenharmony_ci    { sizeof(int),                    esd_proto_sample_free_or_play, "sample free" },
18253a5a1b3Sopenharmony_ci    { sizeof(int),                    esd_proto_sample_free_or_play, "sample play" },                /* 8 */
18353a5a1b3Sopenharmony_ci    { sizeof(int),                    NULL, "sample loop" },
18453a5a1b3Sopenharmony_ci    { sizeof(int),                    NULL, "sample stop" },
18553a5a1b3Sopenharmony_ci    { (size_t) -1,                    NULL, "TODO: sample kill" },
18653a5a1b3Sopenharmony_ci
18753a5a1b3Sopenharmony_ci    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "standby" },
18853a5a1b3Sopenharmony_ci    { ESD_KEY_LEN + sizeof(int),      esd_proto_standby_or_resume, "resume" },                       /* 13 */
18953a5a1b3Sopenharmony_ci
19053a5a1b3Sopenharmony_ci    { ESD_NAME_MAX,                   esd_proto_sample_get_id, "sample getid" },                     /* 14 */
19153a5a1b3Sopenharmony_ci    { ESD_NAME_MAX + 2 * sizeof(int), NULL, "stream filter" },
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_ci    { sizeof(int),                    esd_proto_server_info, "server info" },
19453a5a1b3Sopenharmony_ci    { sizeof(int),                    esd_proto_all_info, "all info" },
19553a5a1b3Sopenharmony_ci    { (size_t) -1,                    NULL, "TODO: subscribe" },
19653a5a1b3Sopenharmony_ci    { (size_t) -1,                    NULL, "TODO: unsubscribe" },
19753a5a1b3Sopenharmony_ci
19853a5a1b3Sopenharmony_ci    { 3 * sizeof(int),                esd_proto_stream_pan, "stream pan"},
19953a5a1b3Sopenharmony_ci    { 3 * sizeof(int),                esd_proto_sample_pan, "sample pan" },
20053a5a1b3Sopenharmony_ci
20153a5a1b3Sopenharmony_ci    { sizeof(int),                    esd_proto_standby_mode, "standby mode" },
20253a5a1b3Sopenharmony_ci    { 0,                              esd_proto_get_latency, "get latency" }
20353a5a1b3Sopenharmony_ci};
20453a5a1b3Sopenharmony_ci
20553a5a1b3Sopenharmony_cistatic void connection_unlink(connection *c) {
20653a5a1b3Sopenharmony_ci    pa_assert(c);
20753a5a1b3Sopenharmony_ci
20853a5a1b3Sopenharmony_ci    if (!c->protocol)
20953a5a1b3Sopenharmony_ci        return;
21053a5a1b3Sopenharmony_ci
21153a5a1b3Sopenharmony_ci    if (c->options) {
21253a5a1b3Sopenharmony_ci        pa_esound_options_unref(c->options);
21353a5a1b3Sopenharmony_ci        c->options = NULL;
21453a5a1b3Sopenharmony_ci    }
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci    if (c->sink_input) {
21753a5a1b3Sopenharmony_ci        pa_sink_input_unlink(c->sink_input);
21853a5a1b3Sopenharmony_ci        pa_sink_input_unref(c->sink_input);
21953a5a1b3Sopenharmony_ci        c->sink_input = NULL;
22053a5a1b3Sopenharmony_ci    }
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ci    if (c->source_output) {
22353a5a1b3Sopenharmony_ci        pa_source_output_unlink(c->source_output);
22453a5a1b3Sopenharmony_ci        pa_source_output_unref(c->source_output);
22553a5a1b3Sopenharmony_ci        c->source_output = NULL;
22653a5a1b3Sopenharmony_ci    }
22753a5a1b3Sopenharmony_ci
22853a5a1b3Sopenharmony_ci    if (c->client) {
22953a5a1b3Sopenharmony_ci        pa_client_free(c->client);
23053a5a1b3Sopenharmony_ci        c->client = NULL;
23153a5a1b3Sopenharmony_ci    }
23253a5a1b3Sopenharmony_ci
23353a5a1b3Sopenharmony_ci    if (c->state == ESD_STREAMING_DATA)
23453a5a1b3Sopenharmony_ci        c->protocol->n_player--;
23553a5a1b3Sopenharmony_ci
23653a5a1b3Sopenharmony_ci    if (c->io) {
23753a5a1b3Sopenharmony_ci        pa_iochannel_free(c->io);
23853a5a1b3Sopenharmony_ci        c->io = NULL;
23953a5a1b3Sopenharmony_ci    }
24053a5a1b3Sopenharmony_ci
24153a5a1b3Sopenharmony_ci    if (c->defer_event) {
24253a5a1b3Sopenharmony_ci        c->protocol->core->mainloop->defer_free(c->defer_event);
24353a5a1b3Sopenharmony_ci        c->defer_event = NULL;
24453a5a1b3Sopenharmony_ci    }
24553a5a1b3Sopenharmony_ci
24653a5a1b3Sopenharmony_ci    if (c->auth_timeout_event) {
24753a5a1b3Sopenharmony_ci        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
24853a5a1b3Sopenharmony_ci        c->auth_timeout_event = NULL;
24953a5a1b3Sopenharmony_ci    }
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    pa_assert_se(pa_idxset_remove_by_data(c->protocol->connections, c, NULL) == c);
25253a5a1b3Sopenharmony_ci    c->protocol = NULL;
25353a5a1b3Sopenharmony_ci    connection_unref(c);
25453a5a1b3Sopenharmony_ci}
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_cistatic void connection_free(pa_object *obj) {
25753a5a1b3Sopenharmony_ci    connection *c = CONNECTION(obj);
25853a5a1b3Sopenharmony_ci    pa_assert(c);
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci    if (c->input_memblockq)
26153a5a1b3Sopenharmony_ci        pa_memblockq_free(c->input_memblockq);
26253a5a1b3Sopenharmony_ci    if (c->output_memblockq)
26353a5a1b3Sopenharmony_ci        pa_memblockq_free(c->output_memblockq);
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci    if (c->playback.current_memblock)
26653a5a1b3Sopenharmony_ci        pa_memblock_unref(c->playback.current_memblock);
26753a5a1b3Sopenharmony_ci
26853a5a1b3Sopenharmony_ci    pa_xfree(c->read_data);
26953a5a1b3Sopenharmony_ci    pa_xfree(c->write_data);
27053a5a1b3Sopenharmony_ci
27153a5a1b3Sopenharmony_ci    if (c->scache.memchunk.memblock)
27253a5a1b3Sopenharmony_ci        pa_memblock_unref(c->scache.memchunk.memblock);
27353a5a1b3Sopenharmony_ci    pa_xfree(c->scache.name);
27453a5a1b3Sopenharmony_ci
27553a5a1b3Sopenharmony_ci    pa_xfree(c->original_name);
27653a5a1b3Sopenharmony_ci    pa_xfree(c);
27753a5a1b3Sopenharmony_ci}
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_cistatic void connection_write_prepare(connection *c, size_t length) {
28053a5a1b3Sopenharmony_ci    size_t t;
28153a5a1b3Sopenharmony_ci    pa_assert(c);
28253a5a1b3Sopenharmony_ci
28353a5a1b3Sopenharmony_ci    t = c->write_data_length+length;
28453a5a1b3Sopenharmony_ci
28553a5a1b3Sopenharmony_ci    if (c->write_data_alloc < t)
28653a5a1b3Sopenharmony_ci        c->write_data = pa_xrealloc(c->write_data, c->write_data_alloc = t);
28753a5a1b3Sopenharmony_ci
28853a5a1b3Sopenharmony_ci    pa_assert(c->write_data);
28953a5a1b3Sopenharmony_ci}
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_cistatic void connection_write(connection *c, const void *data, size_t length) {
29253a5a1b3Sopenharmony_ci    size_t i;
29353a5a1b3Sopenharmony_ci    pa_assert(c);
29453a5a1b3Sopenharmony_ci
29553a5a1b3Sopenharmony_ci    c->protocol->core->mainloop->defer_enable(c->defer_event, 1);
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_ci    connection_write_prepare(c, length);
29853a5a1b3Sopenharmony_ci
29953a5a1b3Sopenharmony_ci    pa_assert(c->write_data);
30053a5a1b3Sopenharmony_ci
30153a5a1b3Sopenharmony_ci    i = c->write_data_length;
30253a5a1b3Sopenharmony_ci    c->write_data_length += length;
30353a5a1b3Sopenharmony_ci
30453a5a1b3Sopenharmony_ci    memcpy((uint8_t*) c->write_data + i, data, length);
30553a5a1b3Sopenharmony_ci}
30653a5a1b3Sopenharmony_ci
30753a5a1b3Sopenharmony_cistatic void format_esd2native(int format, bool swap_bytes, pa_sample_spec *ss) {
30853a5a1b3Sopenharmony_ci    pa_assert(ss);
30953a5a1b3Sopenharmony_ci
31053a5a1b3Sopenharmony_ci    ss->channels = (uint8_t) (((format & ESD_MASK_CHAN) == ESD_STEREO) ? 2 : 1);
31153a5a1b3Sopenharmony_ci    if ((format & ESD_MASK_BITS) == ESD_BITS16)
31253a5a1b3Sopenharmony_ci        ss->format = swap_bytes ? PA_SAMPLE_S16RE : PA_SAMPLE_S16NE;
31353a5a1b3Sopenharmony_ci    else
31453a5a1b3Sopenharmony_ci        ss->format = PA_SAMPLE_U8;
31553a5a1b3Sopenharmony_ci}
31653a5a1b3Sopenharmony_ci
31753a5a1b3Sopenharmony_cistatic int format_native2esd(pa_sample_spec *ss) {
31853a5a1b3Sopenharmony_ci    int format = 0;
31953a5a1b3Sopenharmony_ci
32053a5a1b3Sopenharmony_ci    format = (ss->format == PA_SAMPLE_U8) ? ESD_BITS8 : ESD_BITS16;
32153a5a1b3Sopenharmony_ci    format |= (ss->channels >= 2) ? ESD_STEREO : ESD_MONO;
32253a5a1b3Sopenharmony_ci
32353a5a1b3Sopenharmony_ci    return format;
32453a5a1b3Sopenharmony_ci}
32553a5a1b3Sopenharmony_ci
32653a5a1b3Sopenharmony_ci#define CHECK_VALIDITY(expression, ...) do {            \
32753a5a1b3Sopenharmony_ci        if (PA_UNLIKELY(!(expression))) {               \
32853a5a1b3Sopenharmony_ci            pa_log_warn(__FILE__ ": " __VA_ARGS__);     \
32953a5a1b3Sopenharmony_ci            return -1;                                  \
33053a5a1b3Sopenharmony_ci        }                                               \
33153a5a1b3Sopenharmony_ci    } while(0);
33253a5a1b3Sopenharmony_ci
33353a5a1b3Sopenharmony_ci/*** esound commands ***/
33453a5a1b3Sopenharmony_ci
33553a5a1b3Sopenharmony_cistatic int esd_proto_connect(connection *c, esd_proto_t request, const void *data, size_t length) {
33653a5a1b3Sopenharmony_ci    uint32_t ekey;
33753a5a1b3Sopenharmony_ci    int ok;
33853a5a1b3Sopenharmony_ci
33953a5a1b3Sopenharmony_ci    connection_assert_ref(c);
34053a5a1b3Sopenharmony_ci    pa_assert(data);
34153a5a1b3Sopenharmony_ci    pa_assert(length == (ESD_KEY_LEN + sizeof(uint32_t)));
34253a5a1b3Sopenharmony_ci
34353a5a1b3Sopenharmony_ci    if (!c->authorized && c->options->auth_cookie) {
34453a5a1b3Sopenharmony_ci        const uint8_t*key;
34553a5a1b3Sopenharmony_ci
34653a5a1b3Sopenharmony_ci        if ((key = pa_auth_cookie_read(c->options->auth_cookie, ESD_KEY_LEN)))
34753a5a1b3Sopenharmony_ci            if (memcmp(data, key, ESD_KEY_LEN) == 0)
34853a5a1b3Sopenharmony_ci                c->authorized = true;
34953a5a1b3Sopenharmony_ci    }
35053a5a1b3Sopenharmony_ci
35153a5a1b3Sopenharmony_ci    if (!c->authorized) {
35253a5a1b3Sopenharmony_ci        pa_log("Kicked client with invalid authentication key.");
35353a5a1b3Sopenharmony_ci        return -1;
35453a5a1b3Sopenharmony_ci    }
35553a5a1b3Sopenharmony_ci
35653a5a1b3Sopenharmony_ci    if (c->auth_timeout_event) {
35753a5a1b3Sopenharmony_ci        c->protocol->core->mainloop->time_free(c->auth_timeout_event);
35853a5a1b3Sopenharmony_ci        c->auth_timeout_event = NULL;
35953a5a1b3Sopenharmony_ci    }
36053a5a1b3Sopenharmony_ci
36153a5a1b3Sopenharmony_ci    data = (const char*)data + ESD_KEY_LEN;
36253a5a1b3Sopenharmony_ci
36353a5a1b3Sopenharmony_ci    memcpy(&ekey, data, sizeof(uint32_t));
36453a5a1b3Sopenharmony_ci    if (ekey == ESD_ENDIAN_KEY)
36553a5a1b3Sopenharmony_ci        c->swap_byte_order = false;
36653a5a1b3Sopenharmony_ci    else if (ekey == ESD_SWAP_ENDIAN_KEY)
36753a5a1b3Sopenharmony_ci        c->swap_byte_order = true;
36853a5a1b3Sopenharmony_ci    else {
36953a5a1b3Sopenharmony_ci        pa_log_warn("Client sent invalid endian key");
37053a5a1b3Sopenharmony_ci        return -1;
37153a5a1b3Sopenharmony_ci    }
37253a5a1b3Sopenharmony_ci
37353a5a1b3Sopenharmony_ci    pa_proplist_sets(c->client->proplist, "esound.byte_order", c->swap_byte_order ? "reverse" : "native");
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    ok = 1;
37653a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int));
37753a5a1b3Sopenharmony_ci    return 0;
37853a5a1b3Sopenharmony_ci}
37953a5a1b3Sopenharmony_ci
38053a5a1b3Sopenharmony_cistatic int esd_proto_stream_play(connection *c, esd_proto_t request, const void *data, size_t length) {
38153a5a1b3Sopenharmony_ci    char name[ESD_NAME_MAX], *utf8_name;
38253a5a1b3Sopenharmony_ci    int32_t format, rate;
38353a5a1b3Sopenharmony_ci    pa_sample_spec ss;
38453a5a1b3Sopenharmony_ci    size_t l;
38553a5a1b3Sopenharmony_ci    pa_sink *sink = NULL;
38653a5a1b3Sopenharmony_ci    pa_sink_input_new_data sdata;
38753a5a1b3Sopenharmony_ci    pa_memchunk silence;
38853a5a1b3Sopenharmony_ci
38953a5a1b3Sopenharmony_ci    connection_assert_ref(c);
39053a5a1b3Sopenharmony_ci    pa_assert(data);
39153a5a1b3Sopenharmony_ci    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
39253a5a1b3Sopenharmony_ci
39353a5a1b3Sopenharmony_ci    memcpy(&format, data, sizeof(int32_t));
39453a5a1b3Sopenharmony_ci    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
39553a5a1b3Sopenharmony_ci    data = (const char*) data + sizeof(int32_t);
39653a5a1b3Sopenharmony_ci
39753a5a1b3Sopenharmony_ci    memcpy(&rate, data, sizeof(int32_t));
39853a5a1b3Sopenharmony_ci    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
39953a5a1b3Sopenharmony_ci    data = (const char*) data + sizeof(int32_t);
40053a5a1b3Sopenharmony_ci
40153a5a1b3Sopenharmony_ci    ss.rate = (uint32_t) rate;
40253a5a1b3Sopenharmony_ci    format_esd2native(format, c->swap_byte_order, &ss);
40353a5a1b3Sopenharmony_ci
40453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification");
40553a5a1b3Sopenharmony_ci
40653a5a1b3Sopenharmony_ci    if (c->options->default_sink) {
40753a5a1b3Sopenharmony_ci        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
40853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(sink, "No such sink: %s", pa_strnull(c->options->default_sink));
40953a5a1b3Sopenharmony_ci    }
41053a5a1b3Sopenharmony_ci
41153a5a1b3Sopenharmony_ci    pa_strlcpy(name, data, sizeof(name));
41253a5a1b3Sopenharmony_ci
41353a5a1b3Sopenharmony_ci    utf8_name = pa_utf8_filter(name);
41453a5a1b3Sopenharmony_ci    pa_client_set_name(c->client, utf8_name);
41553a5a1b3Sopenharmony_ci    pa_xfree(utf8_name);
41653a5a1b3Sopenharmony_ci
41753a5a1b3Sopenharmony_ci    c->original_name = pa_xstrdup(name);
41853a5a1b3Sopenharmony_ci
41953a5a1b3Sopenharmony_ci    pa_assert(!c->sink_input && !c->input_memblockq);
42053a5a1b3Sopenharmony_ci
42153a5a1b3Sopenharmony_ci    pa_sink_input_new_data_init(&sdata);
42253a5a1b3Sopenharmony_ci    sdata.driver = __FILE__;
42353a5a1b3Sopenharmony_ci    sdata.module = c->options->module;
42453a5a1b3Sopenharmony_ci    sdata.client = c->client;
42553a5a1b3Sopenharmony_ci    if (sink)
42653a5a1b3Sopenharmony_ci        pa_sink_input_new_data_set_sink(&sdata, sink, false, true);
42753a5a1b3Sopenharmony_ci    pa_sink_input_new_data_set_sample_spec(&sdata, &ss);
42853a5a1b3Sopenharmony_ci
42953a5a1b3Sopenharmony_ci    pa_sink_input_new(&c->sink_input, c->protocol->core, &sdata);
43053a5a1b3Sopenharmony_ci    pa_sink_input_new_data_done(&sdata);
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->sink_input, "Failed to create sink input.");
43353a5a1b3Sopenharmony_ci
43453a5a1b3Sopenharmony_ci    l = (size_t) ((double) pa_bytes_per_second(&ss)*PLAYBACK_BUFFER_SECONDS);
43553a5a1b3Sopenharmony_ci    pa_sink_input_get_silence(c->sink_input, &silence);
43653a5a1b3Sopenharmony_ci    c->input_memblockq = pa_memblockq_new(
43753a5a1b3Sopenharmony_ci            "esound protocol connection input_memblockq",
43853a5a1b3Sopenharmony_ci            0,
43953a5a1b3Sopenharmony_ci            l,
44053a5a1b3Sopenharmony_ci            l,
44153a5a1b3Sopenharmony_ci            &ss,
44253a5a1b3Sopenharmony_ci            (size_t) -1,
44353a5a1b3Sopenharmony_ci            l/PLAYBACK_BUFFER_FRAGMENTS,
44453a5a1b3Sopenharmony_ci            0,
44553a5a1b3Sopenharmony_ci            &silence);
44653a5a1b3Sopenharmony_ci    pa_memblock_unref(silence.memblock);
44753a5a1b3Sopenharmony_ci    pa_iochannel_socket_set_rcvbuf(c->io, l);
44853a5a1b3Sopenharmony_ci
44953a5a1b3Sopenharmony_ci    c->sink_input->parent.process_msg = sink_input_process_msg;
45053a5a1b3Sopenharmony_ci    c->sink_input->pop = sink_input_pop_cb;
45153a5a1b3Sopenharmony_ci    c->sink_input->process_rewind = sink_input_process_rewind_cb;
45253a5a1b3Sopenharmony_ci    c->sink_input->update_max_rewind = sink_input_update_max_rewind_cb;
45353a5a1b3Sopenharmony_ci    c->sink_input->kill = sink_input_kill_cb;
45453a5a1b3Sopenharmony_ci    c->sink_input->userdata = c;
45553a5a1b3Sopenharmony_ci
45653a5a1b3Sopenharmony_ci    pa_sink_input_set_requested_latency(c->sink_input, DEFAULT_SINK_LATENCY);
45753a5a1b3Sopenharmony_ci
45853a5a1b3Sopenharmony_ci    c->state = ESD_STREAMING_DATA;
45953a5a1b3Sopenharmony_ci
46053a5a1b3Sopenharmony_ci    c->protocol->n_player++;
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci    pa_atomic_store(&c->playback.missing, (int) pa_memblockq_pop_missing(c->input_memblockq));
46353a5a1b3Sopenharmony_ci
46453a5a1b3Sopenharmony_ci    pa_sink_input_put(c->sink_input);
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci    return 0;
46753a5a1b3Sopenharmony_ci}
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_cistatic int esd_proto_stream_record(connection *c, esd_proto_t request, const void *data, size_t length) {
47053a5a1b3Sopenharmony_ci    char name[ESD_NAME_MAX], *utf8_name;
47153a5a1b3Sopenharmony_ci    int32_t format, rate;
47253a5a1b3Sopenharmony_ci    pa_source *source = NULL;
47353a5a1b3Sopenharmony_ci    pa_sample_spec ss;
47453a5a1b3Sopenharmony_ci    size_t l;
47553a5a1b3Sopenharmony_ci    pa_source_output_new_data sdata;
47653a5a1b3Sopenharmony_ci
47753a5a1b3Sopenharmony_ci    connection_assert_ref(c);
47853a5a1b3Sopenharmony_ci    pa_assert(data);
47953a5a1b3Sopenharmony_ci    pa_assert(length == (sizeof(int32_t)*2+ESD_NAME_MAX));
48053a5a1b3Sopenharmony_ci
48153a5a1b3Sopenharmony_ci    memcpy(&format, data, sizeof(int32_t));
48253a5a1b3Sopenharmony_ci    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
48353a5a1b3Sopenharmony_ci    data = (const char*) data + sizeof(int32_t);
48453a5a1b3Sopenharmony_ci
48553a5a1b3Sopenharmony_ci    memcpy(&rate, data, sizeof(int32_t));
48653a5a1b3Sopenharmony_ci    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
48753a5a1b3Sopenharmony_ci    data = (const char*) data + sizeof(int32_t);
48853a5a1b3Sopenharmony_ci
48953a5a1b3Sopenharmony_ci    ss.rate = (uint32_t) rate;
49053a5a1b3Sopenharmony_ci    format_esd2native(format, c->swap_byte_order, &ss);
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
49353a5a1b3Sopenharmony_ci
49453a5a1b3Sopenharmony_ci    if (request == ESD_PROTO_STREAM_MON) {
49553a5a1b3Sopenharmony_ci        pa_sink* sink;
49653a5a1b3Sopenharmony_ci
49753a5a1b3Sopenharmony_ci        sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK);
49853a5a1b3Sopenharmony_ci        CHECK_VALIDITY(sink, "No such sink: %s", pa_strnull(c->options->default_sink));
49953a5a1b3Sopenharmony_ci
50053a5a1b3Sopenharmony_ci        source = sink->monitor_source;
50153a5a1b3Sopenharmony_ci        CHECK_VALIDITY(source, "No such source.");
50253a5a1b3Sopenharmony_ci    } else {
50353a5a1b3Sopenharmony_ci        pa_assert(request == ESD_PROTO_STREAM_REC);
50453a5a1b3Sopenharmony_ci
50553a5a1b3Sopenharmony_ci        if (c->options->default_source) {
50653a5a1b3Sopenharmony_ci            source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE);
50753a5a1b3Sopenharmony_ci            CHECK_VALIDITY(source, "No such source: %s", pa_strnull(c->options->default_source));
50853a5a1b3Sopenharmony_ci        }
50953a5a1b3Sopenharmony_ci    }
51053a5a1b3Sopenharmony_ci
51153a5a1b3Sopenharmony_ci    pa_strlcpy(name, data, sizeof(name));
51253a5a1b3Sopenharmony_ci
51353a5a1b3Sopenharmony_ci    utf8_name = pa_utf8_filter(name);
51453a5a1b3Sopenharmony_ci    pa_client_set_name(c->client, utf8_name);
51553a5a1b3Sopenharmony_ci    pa_xfree(utf8_name);
51653a5a1b3Sopenharmony_ci
51753a5a1b3Sopenharmony_ci    c->original_name = pa_xstrdup(name);
51853a5a1b3Sopenharmony_ci
51953a5a1b3Sopenharmony_ci    pa_assert(!c->output_memblockq && !c->source_output);
52053a5a1b3Sopenharmony_ci
52153a5a1b3Sopenharmony_ci    pa_source_output_new_data_init(&sdata);
52253a5a1b3Sopenharmony_ci    sdata.driver = __FILE__;
52353a5a1b3Sopenharmony_ci    sdata.module = c->options->module;
52453a5a1b3Sopenharmony_ci    sdata.client = c->client;
52553a5a1b3Sopenharmony_ci    if (source)
52653a5a1b3Sopenharmony_ci        pa_source_output_new_data_set_source(&sdata, source, false, true);
52753a5a1b3Sopenharmony_ci    pa_source_output_new_data_set_sample_spec(&sdata, &ss);
52853a5a1b3Sopenharmony_ci
52953a5a1b3Sopenharmony_ci    pa_source_output_new(&c->source_output, c->protocol->core, &sdata);
53053a5a1b3Sopenharmony_ci    pa_source_output_new_data_done(&sdata);
53153a5a1b3Sopenharmony_ci
53253a5a1b3Sopenharmony_ci    CHECK_VALIDITY(c->source_output, "Failed to create source output.");
53353a5a1b3Sopenharmony_ci
53453a5a1b3Sopenharmony_ci    l = (size_t) (pa_bytes_per_second(&ss)*RECORD_BUFFER_SECONDS);
53553a5a1b3Sopenharmony_ci    c->output_memblockq = pa_memblockq_new(
53653a5a1b3Sopenharmony_ci            "esound protocol connection output_memblockq",
53753a5a1b3Sopenharmony_ci            0,
53853a5a1b3Sopenharmony_ci            l,
53953a5a1b3Sopenharmony_ci            l,
54053a5a1b3Sopenharmony_ci            &ss,
54153a5a1b3Sopenharmony_ci            1,
54253a5a1b3Sopenharmony_ci            0,
54353a5a1b3Sopenharmony_ci            0,
54453a5a1b3Sopenharmony_ci            NULL);
54553a5a1b3Sopenharmony_ci    pa_iochannel_socket_set_sndbuf(c->io, l);
54653a5a1b3Sopenharmony_ci
54753a5a1b3Sopenharmony_ci    c->source_output->push = source_output_push_cb;
54853a5a1b3Sopenharmony_ci    c->source_output->kill = source_output_kill_cb;
54953a5a1b3Sopenharmony_ci    c->source_output->get_latency = source_output_get_latency_cb;
55053a5a1b3Sopenharmony_ci    c->source_output->userdata = c;
55153a5a1b3Sopenharmony_ci
55253a5a1b3Sopenharmony_ci    pa_source_output_set_requested_latency(c->source_output, DEFAULT_SOURCE_LATENCY);
55353a5a1b3Sopenharmony_ci
55453a5a1b3Sopenharmony_ci    c->state = ESD_STREAMING_DATA;
55553a5a1b3Sopenharmony_ci
55653a5a1b3Sopenharmony_ci    c->protocol->n_player++;
55753a5a1b3Sopenharmony_ci
55853a5a1b3Sopenharmony_ci    pa_source_output_put(c->source_output);
55953a5a1b3Sopenharmony_ci
56053a5a1b3Sopenharmony_ci    return 0;
56153a5a1b3Sopenharmony_ci}
56253a5a1b3Sopenharmony_ci
56353a5a1b3Sopenharmony_cistatic int esd_proto_get_latency(connection *c, esd_proto_t request, const void *data, size_t length) {
56453a5a1b3Sopenharmony_ci    pa_sink *sink;
56553a5a1b3Sopenharmony_ci    int32_t latency;
56653a5a1b3Sopenharmony_ci
56753a5a1b3Sopenharmony_ci    connection_assert_ref(c);
56853a5a1b3Sopenharmony_ci    pa_assert(!data);
56953a5a1b3Sopenharmony_ci    pa_assert(length == 0);
57053a5a1b3Sopenharmony_ci
57153a5a1b3Sopenharmony_ci    if (!(sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
57253a5a1b3Sopenharmony_ci        latency = 0;
57353a5a1b3Sopenharmony_ci    else {
57453a5a1b3Sopenharmony_ci        double usec = (double) pa_sink_get_requested_latency(sink);
57553a5a1b3Sopenharmony_ci        latency = (int) ((usec*44100)/1000000);
57653a5a1b3Sopenharmony_ci    }
57753a5a1b3Sopenharmony_ci
57853a5a1b3Sopenharmony_ci    latency = PA_MAYBE_INT32_SWAP(c->swap_byte_order, latency);
57953a5a1b3Sopenharmony_ci    connection_write(c, &latency, sizeof(int32_t));
58053a5a1b3Sopenharmony_ci
58153a5a1b3Sopenharmony_ci    return 0;
58253a5a1b3Sopenharmony_ci}
58353a5a1b3Sopenharmony_ci
58453a5a1b3Sopenharmony_cistatic int esd_proto_server_info(connection *c, esd_proto_t request, const void *data, size_t length) {
58553a5a1b3Sopenharmony_ci    int32_t rate = 44100, format = ESD_STEREO|ESD_BITS16;
58653a5a1b3Sopenharmony_ci    int32_t response;
58753a5a1b3Sopenharmony_ci    pa_sink *sink;
58853a5a1b3Sopenharmony_ci
58953a5a1b3Sopenharmony_ci    connection_assert_ref(c);
59053a5a1b3Sopenharmony_ci    pa_assert(data);
59153a5a1b3Sopenharmony_ci    pa_assert(length == sizeof(int32_t));
59253a5a1b3Sopenharmony_ci
59353a5a1b3Sopenharmony_ci    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK))) {
59453a5a1b3Sopenharmony_ci        rate = (int32_t) sink->sample_spec.rate;
59553a5a1b3Sopenharmony_ci        format = format_native2esd(&sink->sample_spec);
59653a5a1b3Sopenharmony_ci    }
59753a5a1b3Sopenharmony_ci
59853a5a1b3Sopenharmony_ci    connection_write_prepare(c, sizeof(int32_t) * 3);
59953a5a1b3Sopenharmony_ci
60053a5a1b3Sopenharmony_ci    response = 0;
60153a5a1b3Sopenharmony_ci    connection_write(c, &response, sizeof(int32_t));
60253a5a1b3Sopenharmony_ci    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
60353a5a1b3Sopenharmony_ci    connection_write(c, &rate, sizeof(int32_t));
60453a5a1b3Sopenharmony_ci    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
60553a5a1b3Sopenharmony_ci    connection_write(c, &format, sizeof(int32_t));
60653a5a1b3Sopenharmony_ci
60753a5a1b3Sopenharmony_ci    return 0;
60853a5a1b3Sopenharmony_ci}
60953a5a1b3Sopenharmony_ci
61053a5a1b3Sopenharmony_cistatic int esd_proto_all_info(connection *c, esd_proto_t request, const void *data, size_t length) {
61153a5a1b3Sopenharmony_ci    size_t t, k, s;
61253a5a1b3Sopenharmony_ci    connection *conn;
61353a5a1b3Sopenharmony_ci    uint32_t idx = PA_IDXSET_INVALID;
61453a5a1b3Sopenharmony_ci    unsigned nsamples;
61553a5a1b3Sopenharmony_ci    char terminator[sizeof(int32_t)*6+ESD_NAME_MAX];
61653a5a1b3Sopenharmony_ci
61753a5a1b3Sopenharmony_ci    connection_assert_ref(c);
61853a5a1b3Sopenharmony_ci    pa_assert(data);
61953a5a1b3Sopenharmony_ci    pa_assert(length == sizeof(int32_t));
62053a5a1b3Sopenharmony_ci
62153a5a1b3Sopenharmony_ci    if (esd_proto_server_info(c, request, data, length) < 0)
62253a5a1b3Sopenharmony_ci        return -1;
62353a5a1b3Sopenharmony_ci
62453a5a1b3Sopenharmony_ci    k = sizeof(int32_t)*5+ESD_NAME_MAX;
62553a5a1b3Sopenharmony_ci    s = sizeof(int32_t)*6+ESD_NAME_MAX;
62653a5a1b3Sopenharmony_ci    nsamples = pa_idxset_size(c->protocol->core->scache);
62753a5a1b3Sopenharmony_ci    t = s*(nsamples+1) + k*(c->protocol->n_player+1);
62853a5a1b3Sopenharmony_ci
62953a5a1b3Sopenharmony_ci    connection_write_prepare(c, t);
63053a5a1b3Sopenharmony_ci
63153a5a1b3Sopenharmony_ci    memset(terminator, 0, sizeof(terminator));
63253a5a1b3Sopenharmony_ci
63353a5a1b3Sopenharmony_ci    PA_IDXSET_FOREACH(conn, c->protocol->connections, idx) {
63453a5a1b3Sopenharmony_ci        int32_t id, format = ESD_BITS16 | ESD_STEREO, rate = 44100, lvolume = ESD_VOLUME_BASE, rvolume = ESD_VOLUME_BASE;
63553a5a1b3Sopenharmony_ci        char name[ESD_NAME_MAX];
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci        if (conn->state != ESD_STREAMING_DATA)
63853a5a1b3Sopenharmony_ci            continue;
63953a5a1b3Sopenharmony_ci
64053a5a1b3Sopenharmony_ci        pa_assert(t >= k*2+s);
64153a5a1b3Sopenharmony_ci
64253a5a1b3Sopenharmony_ci        if (conn->sink_input) {
64353a5a1b3Sopenharmony_ci            pa_cvolume volume;
64453a5a1b3Sopenharmony_ci            pa_sink_input_get_volume(conn->sink_input, &volume, true);
64553a5a1b3Sopenharmony_ci            rate = (int32_t) conn->sink_input->sample_spec.rate;
64653a5a1b3Sopenharmony_ci            lvolume = (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
64753a5a1b3Sopenharmony_ci            rvolume = (int32_t) ((volume.values[volume.channels == 2 ? 1 : 0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM);
64853a5a1b3Sopenharmony_ci            format = format_native2esd(&conn->sink_input->sample_spec);
64953a5a1b3Sopenharmony_ci        }
65053a5a1b3Sopenharmony_ci
65153a5a1b3Sopenharmony_ci        /* id */
65253a5a1b3Sopenharmony_ci        id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) (conn->index+1));
65353a5a1b3Sopenharmony_ci        connection_write(c, &id, sizeof(int32_t));
65453a5a1b3Sopenharmony_ci
65553a5a1b3Sopenharmony_ci        /* name */
65653a5a1b3Sopenharmony_ci        memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
65753a5a1b3Sopenharmony_ci        if (conn->original_name)
65853a5a1b3Sopenharmony_ci            strncpy(name, conn->original_name, ESD_NAME_MAX);
65953a5a1b3Sopenharmony_ci        else if (conn->client && pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME))
66053a5a1b3Sopenharmony_ci            strncpy(name, pa_proplist_gets(conn->client->proplist, PA_PROP_APPLICATION_NAME), ESD_NAME_MAX);
66153a5a1b3Sopenharmony_ci        connection_write(c, name, ESD_NAME_MAX);
66253a5a1b3Sopenharmony_ci
66353a5a1b3Sopenharmony_ci        /* rate */
66453a5a1b3Sopenharmony_ci        rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
66553a5a1b3Sopenharmony_ci        connection_write(c, &rate, sizeof(int32_t));
66653a5a1b3Sopenharmony_ci
66753a5a1b3Sopenharmony_ci        /* left */
66853a5a1b3Sopenharmony_ci        lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, lvolume);
66953a5a1b3Sopenharmony_ci        connection_write(c, &lvolume, sizeof(int32_t));
67053a5a1b3Sopenharmony_ci
67153a5a1b3Sopenharmony_ci        /*right*/
67253a5a1b3Sopenharmony_ci        rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rvolume);
67353a5a1b3Sopenharmony_ci        connection_write(c, &rvolume, sizeof(int32_t));
67453a5a1b3Sopenharmony_ci
67553a5a1b3Sopenharmony_ci        /*format*/
67653a5a1b3Sopenharmony_ci        format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
67753a5a1b3Sopenharmony_ci        connection_write(c, &format, sizeof(int32_t));
67853a5a1b3Sopenharmony_ci
67953a5a1b3Sopenharmony_ci        t -= k;
68053a5a1b3Sopenharmony_ci    }
68153a5a1b3Sopenharmony_ci
68253a5a1b3Sopenharmony_ci    pa_assert(t == s*(nsamples+1)+k);
68353a5a1b3Sopenharmony_ci    t -= k;
68453a5a1b3Sopenharmony_ci
68553a5a1b3Sopenharmony_ci    connection_write(c, terminator, k);
68653a5a1b3Sopenharmony_ci
68753a5a1b3Sopenharmony_ci    if (nsamples) {
68853a5a1b3Sopenharmony_ci        pa_scache_entry *ce;
68953a5a1b3Sopenharmony_ci
69053a5a1b3Sopenharmony_ci        idx = PA_IDXSET_INVALID;
69153a5a1b3Sopenharmony_ci
69253a5a1b3Sopenharmony_ci        PA_IDXSET_FOREACH(ce, c->protocol->core->scache, idx) {
69353a5a1b3Sopenharmony_ci            int32_t id, rate, lvolume, rvolume, format, len;
69453a5a1b3Sopenharmony_ci            char name[ESD_NAME_MAX];
69553a5a1b3Sopenharmony_ci            pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } };
69653a5a1b3Sopenharmony_ci            pa_cvolume volume;
69753a5a1b3Sopenharmony_ci            pa_sample_spec ss;
69853a5a1b3Sopenharmony_ci
69953a5a1b3Sopenharmony_ci            pa_assert(t >= s*2);
70053a5a1b3Sopenharmony_ci
70153a5a1b3Sopenharmony_ci            if (ce->volume_is_set) {
70253a5a1b3Sopenharmony_ci                volume = ce->volume;
70353a5a1b3Sopenharmony_ci                pa_cvolume_remap(&volume, &ce->channel_map, &stereo);
70453a5a1b3Sopenharmony_ci            } else
70553a5a1b3Sopenharmony_ci                pa_cvolume_reset(&volume, 2);
70653a5a1b3Sopenharmony_ci
70753a5a1b3Sopenharmony_ci            if (ce->memchunk.memblock)
70853a5a1b3Sopenharmony_ci                ss = ce->sample_spec;
70953a5a1b3Sopenharmony_ci            else {
71053a5a1b3Sopenharmony_ci                ss.format = PA_SAMPLE_S16NE;
71153a5a1b3Sopenharmony_ci                ss.rate = 44100;
71253a5a1b3Sopenharmony_ci                ss.channels = 2;
71353a5a1b3Sopenharmony_ci            }
71453a5a1b3Sopenharmony_ci
71553a5a1b3Sopenharmony_ci            /* id */
71653a5a1b3Sopenharmony_ci            id = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) (ce->index+1));
71753a5a1b3Sopenharmony_ci            connection_write(c, &id, sizeof(int32_t));
71853a5a1b3Sopenharmony_ci
71953a5a1b3Sopenharmony_ci            /* name */
72053a5a1b3Sopenharmony_ci            memset(name, 0, ESD_NAME_MAX); /* don't leak old data */
72153a5a1b3Sopenharmony_ci            if (strncmp(ce->name, SCACHE_PREFIX, sizeof(SCACHE_PREFIX)-1) == 0)
72253a5a1b3Sopenharmony_ci                strncpy(name, ce->name+sizeof(SCACHE_PREFIX)-1, ESD_NAME_MAX);
72353a5a1b3Sopenharmony_ci            else
72453a5a1b3Sopenharmony_ci                pa_snprintf(name, ESD_NAME_MAX, "native.%s", ce->name);
72553a5a1b3Sopenharmony_ci            connection_write(c, name, ESD_NAME_MAX);
72653a5a1b3Sopenharmony_ci
72753a5a1b3Sopenharmony_ci            /* rate */
72853a5a1b3Sopenharmony_ci            rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ss.rate);
72953a5a1b3Sopenharmony_ci            connection_write(c, &rate, sizeof(int32_t));
73053a5a1b3Sopenharmony_ci
73153a5a1b3Sopenharmony_ci            /* left */
73253a5a1b3Sopenharmony_ci            lvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((volume.values[0]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
73353a5a1b3Sopenharmony_ci            connection_write(c, &lvolume, sizeof(int32_t));
73453a5a1b3Sopenharmony_ci
73553a5a1b3Sopenharmony_ci            /*right*/
73653a5a1b3Sopenharmony_ci            rvolume = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int32_t) ((volume.values[1]*ESD_VOLUME_BASE)/PA_VOLUME_NORM));
73753a5a1b3Sopenharmony_ci            connection_write(c, &rvolume, sizeof(int32_t));
73853a5a1b3Sopenharmony_ci
73953a5a1b3Sopenharmony_ci            /*format*/
74053a5a1b3Sopenharmony_ci            format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format_native2esd(&ss));
74153a5a1b3Sopenharmony_ci            connection_write(c, &format, sizeof(int32_t));
74253a5a1b3Sopenharmony_ci
74353a5a1b3Sopenharmony_ci            /*length*/
74453a5a1b3Sopenharmony_ci            len = PA_MAYBE_INT32_SWAP(c->swap_byte_order, (int) ce->memchunk.length);
74553a5a1b3Sopenharmony_ci            connection_write(c, &len, sizeof(int32_t));
74653a5a1b3Sopenharmony_ci
74753a5a1b3Sopenharmony_ci            t -= s;
74853a5a1b3Sopenharmony_ci        }
74953a5a1b3Sopenharmony_ci    }
75053a5a1b3Sopenharmony_ci
75153a5a1b3Sopenharmony_ci    pa_assert(t == s);
75253a5a1b3Sopenharmony_ci
75353a5a1b3Sopenharmony_ci    connection_write(c, terminator, s);
75453a5a1b3Sopenharmony_ci
75553a5a1b3Sopenharmony_ci    return 0;
75653a5a1b3Sopenharmony_ci}
75753a5a1b3Sopenharmony_ci
75853a5a1b3Sopenharmony_cistatic int esd_proto_stream_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
75953a5a1b3Sopenharmony_ci    int32_t ok;
76053a5a1b3Sopenharmony_ci    uint32_t idx, lvolume, rvolume;
76153a5a1b3Sopenharmony_ci    connection *conn;
76253a5a1b3Sopenharmony_ci
76353a5a1b3Sopenharmony_ci    connection_assert_ref(c);
76453a5a1b3Sopenharmony_ci    pa_assert(data);
76553a5a1b3Sopenharmony_ci    pa_assert(length == sizeof(int32_t)*3);
76653a5a1b3Sopenharmony_ci
76753a5a1b3Sopenharmony_ci    memcpy(&idx, data, sizeof(uint32_t));
76853a5a1b3Sopenharmony_ci    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
76953a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(uint32_t);
77053a5a1b3Sopenharmony_ci
77153a5a1b3Sopenharmony_ci    memcpy(&lvolume, data, sizeof(uint32_t));
77253a5a1b3Sopenharmony_ci    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
77353a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(uint32_t);
77453a5a1b3Sopenharmony_ci
77553a5a1b3Sopenharmony_ci    memcpy(&rvolume, data, sizeof(uint32_t));
77653a5a1b3Sopenharmony_ci    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
77753a5a1b3Sopenharmony_ci
77853a5a1b3Sopenharmony_ci    if ((conn = pa_idxset_get_by_index(c->protocol->connections, idx)) && conn->sink_input) {
77953a5a1b3Sopenharmony_ci        pa_cvolume volume;
78053a5a1b3Sopenharmony_ci        volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
78153a5a1b3Sopenharmony_ci        volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
78253a5a1b3Sopenharmony_ci        volume.channels = conn->sink_input->sample_spec.channels;
78353a5a1b3Sopenharmony_ci
78453a5a1b3Sopenharmony_ci        pa_sink_input_set_volume(conn->sink_input, &volume, true, true);
78553a5a1b3Sopenharmony_ci        ok = 1;
78653a5a1b3Sopenharmony_ci    } else
78753a5a1b3Sopenharmony_ci        ok = 0;
78853a5a1b3Sopenharmony_ci
78953a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int32_t));
79053a5a1b3Sopenharmony_ci
79153a5a1b3Sopenharmony_ci    return 0;
79253a5a1b3Sopenharmony_ci}
79353a5a1b3Sopenharmony_ci
79453a5a1b3Sopenharmony_cistatic int esd_proto_sample_pan(connection *c, esd_proto_t request, const void *data, size_t length) {
79553a5a1b3Sopenharmony_ci    int32_t ok = 0;
79653a5a1b3Sopenharmony_ci    uint32_t idx, lvolume, rvolume;
79753a5a1b3Sopenharmony_ci    pa_cvolume volume;
79853a5a1b3Sopenharmony_ci    pa_scache_entry *ce;
79953a5a1b3Sopenharmony_ci
80053a5a1b3Sopenharmony_ci    connection_assert_ref(c);
80153a5a1b3Sopenharmony_ci    pa_assert(data);
80253a5a1b3Sopenharmony_ci    pa_assert(length == sizeof(int32_t)*3);
80353a5a1b3Sopenharmony_ci
80453a5a1b3Sopenharmony_ci    memcpy(&idx, data, sizeof(uint32_t));
80553a5a1b3Sopenharmony_ci    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
80653a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(uint32_t);
80753a5a1b3Sopenharmony_ci
80853a5a1b3Sopenharmony_ci    memcpy(&lvolume, data, sizeof(uint32_t));
80953a5a1b3Sopenharmony_ci    lvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, lvolume);
81053a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(uint32_t);
81153a5a1b3Sopenharmony_ci
81253a5a1b3Sopenharmony_ci    memcpy(&rvolume, data, sizeof(uint32_t));
81353a5a1b3Sopenharmony_ci    rvolume = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, rvolume);
81453a5a1b3Sopenharmony_ci
81553a5a1b3Sopenharmony_ci    volume.values[0] = (lvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
81653a5a1b3Sopenharmony_ci    volume.values[1] = (rvolume*PA_VOLUME_NORM)/ESD_VOLUME_BASE;
81753a5a1b3Sopenharmony_ci    volume.channels = 2;
81853a5a1b3Sopenharmony_ci
81953a5a1b3Sopenharmony_ci    if ((ce = pa_idxset_get_by_index(c->protocol->core->scache, idx))) {
82053a5a1b3Sopenharmony_ci        pa_channel_map stereo = { .channels = 2, .map = { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } };
82153a5a1b3Sopenharmony_ci
82253a5a1b3Sopenharmony_ci        pa_cvolume_remap(&volume, &stereo, &ce->channel_map);
82353a5a1b3Sopenharmony_ci        ce->volume = volume;
82453a5a1b3Sopenharmony_ci        ce->volume_is_set = true;
82553a5a1b3Sopenharmony_ci        ok = 1;
82653a5a1b3Sopenharmony_ci    }
82753a5a1b3Sopenharmony_ci
82853a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int32_t));
82953a5a1b3Sopenharmony_ci
83053a5a1b3Sopenharmony_ci    return 0;
83153a5a1b3Sopenharmony_ci}
83253a5a1b3Sopenharmony_ci
83353a5a1b3Sopenharmony_cistatic int esd_proto_sample_cache(connection *c, esd_proto_t request, const void *data, size_t length) {
83453a5a1b3Sopenharmony_ci    pa_sample_spec ss;
83553a5a1b3Sopenharmony_ci    int32_t format, rate, sc_length;
83653a5a1b3Sopenharmony_ci    uint32_t idx;
83753a5a1b3Sopenharmony_ci    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
83853a5a1b3Sopenharmony_ci
83953a5a1b3Sopenharmony_ci    connection_assert_ref(c);
84053a5a1b3Sopenharmony_ci    pa_assert(data);
84153a5a1b3Sopenharmony_ci    pa_assert(length == (ESD_NAME_MAX+3*sizeof(int32_t)));
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci    memcpy(&format, data, sizeof(int32_t));
84453a5a1b3Sopenharmony_ci    format = PA_MAYBE_INT32_SWAP(c->swap_byte_order, format);
84553a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(int32_t);
84653a5a1b3Sopenharmony_ci
84753a5a1b3Sopenharmony_ci    memcpy(&rate, data, sizeof(int32_t));
84853a5a1b3Sopenharmony_ci    rate = PA_MAYBE_INT32_SWAP(c->swap_byte_order, rate);
84953a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(int32_t);
85053a5a1b3Sopenharmony_ci
85153a5a1b3Sopenharmony_ci    ss.rate = (uint32_t) rate;
85253a5a1b3Sopenharmony_ci    format_esd2native(format, c->swap_byte_order, &ss);
85353a5a1b3Sopenharmony_ci
85453a5a1b3Sopenharmony_ci    CHECK_VALIDITY(pa_sample_spec_valid(&ss), "Invalid sample specification.");
85553a5a1b3Sopenharmony_ci
85653a5a1b3Sopenharmony_ci    memcpy(&sc_length, data, sizeof(int32_t));
85753a5a1b3Sopenharmony_ci    sc_length = PA_MAYBE_INT32_SWAP(c->swap_byte_order, sc_length);
85853a5a1b3Sopenharmony_ci    data = (const char*)data + sizeof(int32_t);
85953a5a1b3Sopenharmony_ci
86053a5a1b3Sopenharmony_ci    CHECK_VALIDITY(sc_length <= MAX_CACHE_SAMPLE_SIZE, "Sample too large (%d bytes).", (int)sc_length);
86153a5a1b3Sopenharmony_ci
86253a5a1b3Sopenharmony_ci    strcpy(name, SCACHE_PREFIX);
86353a5a1b3Sopenharmony_ci    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
86453a5a1b3Sopenharmony_ci
86553a5a1b3Sopenharmony_ci    CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
86653a5a1b3Sopenharmony_ci
86753a5a1b3Sopenharmony_ci    pa_assert(!c->scache.memchunk.memblock);
86853a5a1b3Sopenharmony_ci    c->scache.memchunk.memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) sc_length);
86953a5a1b3Sopenharmony_ci    c->scache.memchunk.index = 0;
87053a5a1b3Sopenharmony_ci    c->scache.memchunk.length = (size_t) sc_length;
87153a5a1b3Sopenharmony_ci    c->scache.sample_spec = ss;
87253a5a1b3Sopenharmony_ci    pa_assert(!c->scache.name);
87353a5a1b3Sopenharmony_ci    c->scache.name = pa_xstrdup(name);
87453a5a1b3Sopenharmony_ci
87553a5a1b3Sopenharmony_ci    c->state = ESD_CACHING_SAMPLE;
87653a5a1b3Sopenharmony_ci
87753a5a1b3Sopenharmony_ci    pa_scache_add_item(c->protocol->core, c->scache.name, NULL, NULL, NULL, c->client->proplist, &idx);
87853a5a1b3Sopenharmony_ci
87953a5a1b3Sopenharmony_ci    idx += 1;
88053a5a1b3Sopenharmony_ci    connection_write(c, &idx, sizeof(uint32_t));
88153a5a1b3Sopenharmony_ci
88253a5a1b3Sopenharmony_ci    return 0;
88353a5a1b3Sopenharmony_ci}
88453a5a1b3Sopenharmony_ci
88553a5a1b3Sopenharmony_cistatic int esd_proto_sample_get_id(connection *c, esd_proto_t request, const void *data, size_t length) {
88653a5a1b3Sopenharmony_ci    int32_t ok;
88753a5a1b3Sopenharmony_ci    uint32_t idx;
88853a5a1b3Sopenharmony_ci    char name[ESD_NAME_MAX+sizeof(SCACHE_PREFIX)-1];
88953a5a1b3Sopenharmony_ci
89053a5a1b3Sopenharmony_ci    connection_assert_ref(c);
89153a5a1b3Sopenharmony_ci    pa_assert(data);
89253a5a1b3Sopenharmony_ci    pa_assert(length == ESD_NAME_MAX);
89353a5a1b3Sopenharmony_ci
89453a5a1b3Sopenharmony_ci    strcpy(name, SCACHE_PREFIX);
89553a5a1b3Sopenharmony_ci    pa_strlcpy(name+sizeof(SCACHE_PREFIX)-1, data, ESD_NAME_MAX);
89653a5a1b3Sopenharmony_ci
89753a5a1b3Sopenharmony_ci    CHECK_VALIDITY(pa_utf8_valid(name), "Invalid UTF8 in sample name.");
89853a5a1b3Sopenharmony_ci
89953a5a1b3Sopenharmony_ci    ok = -1;
90053a5a1b3Sopenharmony_ci    if ((idx = pa_scache_get_id_by_name(c->protocol->core, name)) != PA_IDXSET_INVALID)
90153a5a1b3Sopenharmony_ci        ok = (int32_t) idx + 1;
90253a5a1b3Sopenharmony_ci
90353a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int32_t));
90453a5a1b3Sopenharmony_ci
90553a5a1b3Sopenharmony_ci    return 0;
90653a5a1b3Sopenharmony_ci}
90753a5a1b3Sopenharmony_ci
90853a5a1b3Sopenharmony_cistatic int esd_proto_sample_free_or_play(connection *c, esd_proto_t request, const void *data, size_t length) {
90953a5a1b3Sopenharmony_ci    int32_t ok;
91053a5a1b3Sopenharmony_ci    const char *name;
91153a5a1b3Sopenharmony_ci    uint32_t idx;
91253a5a1b3Sopenharmony_ci
91353a5a1b3Sopenharmony_ci    connection_assert_ref(c);
91453a5a1b3Sopenharmony_ci    pa_assert(data);
91553a5a1b3Sopenharmony_ci    pa_assert(length == sizeof(int32_t));
91653a5a1b3Sopenharmony_ci
91753a5a1b3Sopenharmony_ci    memcpy(&idx, data, sizeof(uint32_t));
91853a5a1b3Sopenharmony_ci    idx = PA_MAYBE_UINT32_SWAP(c->swap_byte_order, idx) - 1;
91953a5a1b3Sopenharmony_ci
92053a5a1b3Sopenharmony_ci    ok = 0;
92153a5a1b3Sopenharmony_ci
92253a5a1b3Sopenharmony_ci    if ((name = pa_scache_get_name_by_id(c->protocol->core, idx))) {
92353a5a1b3Sopenharmony_ci        if (request == ESD_PROTO_SAMPLE_PLAY) {
92453a5a1b3Sopenharmony_ci            pa_sink *sink;
92553a5a1b3Sopenharmony_ci
92653a5a1b3Sopenharmony_ci            if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
92753a5a1b3Sopenharmony_ci                if (pa_scache_play_item(c->protocol->core, name, sink, PA_VOLUME_NORM, c->client->proplist, NULL) >= 0)
92853a5a1b3Sopenharmony_ci                    ok = (int32_t) idx + 1;
92953a5a1b3Sopenharmony_ci        } else {
93053a5a1b3Sopenharmony_ci            pa_assert(request == ESD_PROTO_SAMPLE_FREE);
93153a5a1b3Sopenharmony_ci
93253a5a1b3Sopenharmony_ci            if (pa_scache_remove_item(c->protocol->core, name) >= 0)
93353a5a1b3Sopenharmony_ci                ok = (int32_t) idx + 1;
93453a5a1b3Sopenharmony_ci        }
93553a5a1b3Sopenharmony_ci    }
93653a5a1b3Sopenharmony_ci
93753a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int32_t));
93853a5a1b3Sopenharmony_ci
93953a5a1b3Sopenharmony_ci    return 0;
94053a5a1b3Sopenharmony_ci}
94153a5a1b3Sopenharmony_ci
94253a5a1b3Sopenharmony_cistatic int esd_proto_standby_or_resume(connection *c, esd_proto_t request, const void *data, size_t length) {
94353a5a1b3Sopenharmony_ci    int32_t ok = 1;
94453a5a1b3Sopenharmony_ci
94553a5a1b3Sopenharmony_ci    connection_assert_ref(c);
94653a5a1b3Sopenharmony_ci
94753a5a1b3Sopenharmony_ci    connection_write_prepare(c, sizeof(int32_t) * 2);
94853a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int32_t));
94953a5a1b3Sopenharmony_ci
95053a5a1b3Sopenharmony_ci    pa_log_debug("%s of all sinks and sources requested by client %" PRIu32 ".",
95153a5a1b3Sopenharmony_ci                 request == ESD_PROTO_STANDBY ? "Suspending" : "Resuming", c->client->index);
95253a5a1b3Sopenharmony_ci
95353a5a1b3Sopenharmony_ci    if (request == ESD_PROTO_STANDBY) {
95453a5a1b3Sopenharmony_ci        ok = pa_sink_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
95553a5a1b3Sopenharmony_ci        ok &= pa_source_suspend_all(c->protocol->core, true, PA_SUSPEND_USER) >= 0;
95653a5a1b3Sopenharmony_ci    } else {
95753a5a1b3Sopenharmony_ci        pa_assert(request == ESD_PROTO_RESUME);
95853a5a1b3Sopenharmony_ci        ok = pa_sink_suspend_all(c->protocol->core, false, PA_SUSPEND_USER) >= 0;
95953a5a1b3Sopenharmony_ci        ok &= pa_source_suspend_all(c->protocol->core, false, PA_SUSPEND_USER) >= 0;
96053a5a1b3Sopenharmony_ci    }
96153a5a1b3Sopenharmony_ci
96253a5a1b3Sopenharmony_ci    connection_write(c, &ok, sizeof(int32_t));
96353a5a1b3Sopenharmony_ci
96453a5a1b3Sopenharmony_ci    return 0;
96553a5a1b3Sopenharmony_ci}
96653a5a1b3Sopenharmony_ci
96753a5a1b3Sopenharmony_cistatic int esd_proto_standby_mode(connection *c, esd_proto_t request, const void *data, size_t length) {
96853a5a1b3Sopenharmony_ci    int32_t mode;
96953a5a1b3Sopenharmony_ci    pa_sink *sink;
97053a5a1b3Sopenharmony_ci    pa_source *source;
97153a5a1b3Sopenharmony_ci
97253a5a1b3Sopenharmony_ci    connection_assert_ref(c);
97353a5a1b3Sopenharmony_ci
97453a5a1b3Sopenharmony_ci    mode = ESM_RUNNING;
97553a5a1b3Sopenharmony_ci
97653a5a1b3Sopenharmony_ci    if ((sink = pa_namereg_get(c->protocol->core, c->options->default_sink, PA_NAMEREG_SINK)))
97753a5a1b3Sopenharmony_ci        if (sink->state == PA_SINK_SUSPENDED)
97853a5a1b3Sopenharmony_ci            mode = ESM_ON_STANDBY;
97953a5a1b3Sopenharmony_ci
98053a5a1b3Sopenharmony_ci    if ((source = pa_namereg_get(c->protocol->core, c->options->default_source, PA_NAMEREG_SOURCE)))
98153a5a1b3Sopenharmony_ci        if (source->state == PA_SOURCE_SUSPENDED)
98253a5a1b3Sopenharmony_ci            mode = ESM_ON_STANDBY;
98353a5a1b3Sopenharmony_ci
98453a5a1b3Sopenharmony_ci    mode = PA_MAYBE_INT32_SWAP(c->swap_byte_order, mode);
98553a5a1b3Sopenharmony_ci
98653a5a1b3Sopenharmony_ci    connection_write(c, &mode, sizeof(mode));
98753a5a1b3Sopenharmony_ci    return 0;
98853a5a1b3Sopenharmony_ci}
98953a5a1b3Sopenharmony_ci
99053a5a1b3Sopenharmony_ci/*** client callbacks ***/
99153a5a1b3Sopenharmony_ci
99253a5a1b3Sopenharmony_cistatic void client_kill_cb(pa_client *c) {
99353a5a1b3Sopenharmony_ci    pa_assert(c);
99453a5a1b3Sopenharmony_ci
99553a5a1b3Sopenharmony_ci    connection_unlink(CONNECTION(c->userdata));
99653a5a1b3Sopenharmony_ci}
99753a5a1b3Sopenharmony_ci
99853a5a1b3Sopenharmony_ci/*** pa_iochannel callbacks ***/
99953a5a1b3Sopenharmony_ci
100053a5a1b3Sopenharmony_cistatic int do_read(connection *c) {
100153a5a1b3Sopenharmony_ci    connection_assert_ref(c);
100253a5a1b3Sopenharmony_ci
100353a5a1b3Sopenharmony_ci/*     pa_log("READ"); */
100453a5a1b3Sopenharmony_ci
100553a5a1b3Sopenharmony_ci    if (c->state == ESD_NEXT_REQUEST) {
100653a5a1b3Sopenharmony_ci        ssize_t r;
100753a5a1b3Sopenharmony_ci        pa_assert(c->read_data_length < sizeof(c->request));
100853a5a1b3Sopenharmony_ci
100953a5a1b3Sopenharmony_ci        if ((r = pa_iochannel_read(c->io,
101053a5a1b3Sopenharmony_ci                                   ((uint8_t*) &c->request) + c->read_data_length,
101153a5a1b3Sopenharmony_ci                                   sizeof(c->request) - c->read_data_length)) <= 0) {
101253a5a1b3Sopenharmony_ci
101353a5a1b3Sopenharmony_ci            if (r < 0 && errno == EAGAIN)
101453a5a1b3Sopenharmony_ci                return 0;
101553a5a1b3Sopenharmony_ci
101653a5a1b3Sopenharmony_ci            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
101753a5a1b3Sopenharmony_ci            return -1;
101853a5a1b3Sopenharmony_ci        }
101953a5a1b3Sopenharmony_ci
102053a5a1b3Sopenharmony_ci        c->read_data_length += (size_t) r;
102153a5a1b3Sopenharmony_ci
102253a5a1b3Sopenharmony_ci        if (c->read_data_length >= sizeof(c->request)) {
102353a5a1b3Sopenharmony_ci            struct proto_handler *handler;
102453a5a1b3Sopenharmony_ci
102553a5a1b3Sopenharmony_ci            c->request = PA_MAYBE_INT32_SWAP(c->swap_byte_order, c->request);
102653a5a1b3Sopenharmony_ci
102753a5a1b3Sopenharmony_ci            if (c->request < ESD_PROTO_CONNECT || c->request >= ESD_PROTO_MAX) {
102853a5a1b3Sopenharmony_ci                pa_log("received invalid request.");
102953a5a1b3Sopenharmony_ci                return -1;
103053a5a1b3Sopenharmony_ci            }
103153a5a1b3Sopenharmony_ci
103253a5a1b3Sopenharmony_ci            handler = proto_map+c->request;
103353a5a1b3Sopenharmony_ci
103453a5a1b3Sopenharmony_ci/*             pa_log("executing request #%u", c->request); */
103553a5a1b3Sopenharmony_ci
103653a5a1b3Sopenharmony_ci            if (!handler->proc) {
103753a5a1b3Sopenharmony_ci                pa_log("received unimplemented request #%u.", c->request);
103853a5a1b3Sopenharmony_ci                return -1;
103953a5a1b3Sopenharmony_ci            }
104053a5a1b3Sopenharmony_ci
104153a5a1b3Sopenharmony_ci            if (handler->data_length == 0) {
104253a5a1b3Sopenharmony_ci                c->read_data_length = 0;
104353a5a1b3Sopenharmony_ci
104453a5a1b3Sopenharmony_ci                if (handler->proc(c, c->request, NULL, 0) < 0)
104553a5a1b3Sopenharmony_ci                    return -1;
104653a5a1b3Sopenharmony_ci
104753a5a1b3Sopenharmony_ci            } else {
104853a5a1b3Sopenharmony_ci                if (c->read_data_alloc < handler->data_length)
104953a5a1b3Sopenharmony_ci                    c->read_data = pa_xrealloc(c->read_data, c->read_data_alloc = handler->data_length);
105053a5a1b3Sopenharmony_ci                pa_assert(c->read_data);
105153a5a1b3Sopenharmony_ci
105253a5a1b3Sopenharmony_ci                c->state = ESD_NEEDS_REQDATA;
105353a5a1b3Sopenharmony_ci                c->read_data_length = 0;
105453a5a1b3Sopenharmony_ci            }
105553a5a1b3Sopenharmony_ci        }
105653a5a1b3Sopenharmony_ci
105753a5a1b3Sopenharmony_ci    } else if (c->state == ESD_NEEDS_REQDATA) {
105853a5a1b3Sopenharmony_ci        ssize_t r;
105953a5a1b3Sopenharmony_ci        struct proto_handler *handler = proto_map+c->request;
106053a5a1b3Sopenharmony_ci
106153a5a1b3Sopenharmony_ci        pa_assert(handler->proc);
106253a5a1b3Sopenharmony_ci
106353a5a1b3Sopenharmony_ci        pa_assert(c->read_data && c->read_data_length < handler->data_length);
106453a5a1b3Sopenharmony_ci
106553a5a1b3Sopenharmony_ci        if ((r = pa_iochannel_read(c->io,
106653a5a1b3Sopenharmony_ci                                   (uint8_t*) c->read_data + c->read_data_length,
106753a5a1b3Sopenharmony_ci                                   handler->data_length - c->read_data_length)) <= 0) {
106853a5a1b3Sopenharmony_ci
106953a5a1b3Sopenharmony_ci            if (r < 0 && errno == EAGAIN)
107053a5a1b3Sopenharmony_ci                return 0;
107153a5a1b3Sopenharmony_ci
107253a5a1b3Sopenharmony_ci            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
107353a5a1b3Sopenharmony_ci            return -1;
107453a5a1b3Sopenharmony_ci        }
107553a5a1b3Sopenharmony_ci
107653a5a1b3Sopenharmony_ci        c->read_data_length += (size_t) r;
107753a5a1b3Sopenharmony_ci        if (c->read_data_length >= handler->data_length) {
107853a5a1b3Sopenharmony_ci            size_t l = c->read_data_length;
107953a5a1b3Sopenharmony_ci            pa_assert(handler->proc);
108053a5a1b3Sopenharmony_ci
108153a5a1b3Sopenharmony_ci            c->state = ESD_NEXT_REQUEST;
108253a5a1b3Sopenharmony_ci            c->read_data_length = 0;
108353a5a1b3Sopenharmony_ci
108453a5a1b3Sopenharmony_ci            if (handler->proc(c, c->request, c->read_data, l) < 0)
108553a5a1b3Sopenharmony_ci                return -1;
108653a5a1b3Sopenharmony_ci        }
108753a5a1b3Sopenharmony_ci    } else if (c->state == ESD_CACHING_SAMPLE) {
108853a5a1b3Sopenharmony_ci        ssize_t r;
108953a5a1b3Sopenharmony_ci        void *p;
109053a5a1b3Sopenharmony_ci
109153a5a1b3Sopenharmony_ci        pa_assert(c->scache.memchunk.memblock);
109253a5a1b3Sopenharmony_ci        pa_assert(c->scache.name);
109353a5a1b3Sopenharmony_ci        pa_assert(c->scache.memchunk.index < c->scache.memchunk.length);
109453a5a1b3Sopenharmony_ci
109553a5a1b3Sopenharmony_ci        p = pa_memblock_acquire(c->scache.memchunk.memblock);
109653a5a1b3Sopenharmony_ci        r = pa_iochannel_read(c->io, (uint8_t*) p+c->scache.memchunk.index, c->scache.memchunk.length-c->scache.memchunk.index);
109753a5a1b3Sopenharmony_ci        pa_memblock_release(c->scache.memchunk.memblock);
109853a5a1b3Sopenharmony_ci
109953a5a1b3Sopenharmony_ci        if (r <= 0) {
110053a5a1b3Sopenharmony_ci            if (r < 0 && errno == EAGAIN)
110153a5a1b3Sopenharmony_ci                return 0;
110253a5a1b3Sopenharmony_ci
110353a5a1b3Sopenharmony_ci            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
110453a5a1b3Sopenharmony_ci            return -1;
110553a5a1b3Sopenharmony_ci        }
110653a5a1b3Sopenharmony_ci
110753a5a1b3Sopenharmony_ci        c->scache.memchunk.index += (size_t) r;
110853a5a1b3Sopenharmony_ci        pa_assert(c->scache.memchunk.index <= c->scache.memchunk.length);
110953a5a1b3Sopenharmony_ci
111053a5a1b3Sopenharmony_ci        if (c->scache.memchunk.index == c->scache.memchunk.length) {
111153a5a1b3Sopenharmony_ci            uint32_t idx;
111253a5a1b3Sopenharmony_ci
111353a5a1b3Sopenharmony_ci            c->scache.memchunk.index = 0;
111453a5a1b3Sopenharmony_ci            pa_scache_add_item(c->protocol->core, c->scache.name, &c->scache.sample_spec, NULL, &c->scache.memchunk, c->client->proplist, &idx);
111553a5a1b3Sopenharmony_ci
111653a5a1b3Sopenharmony_ci            pa_memblock_unref(c->scache.memchunk.memblock);
111753a5a1b3Sopenharmony_ci            pa_memchunk_reset(&c->scache.memchunk);
111853a5a1b3Sopenharmony_ci
111953a5a1b3Sopenharmony_ci            pa_xfree(c->scache.name);
112053a5a1b3Sopenharmony_ci            c->scache.name = NULL;
112153a5a1b3Sopenharmony_ci
112253a5a1b3Sopenharmony_ci            c->state = ESD_NEXT_REQUEST;
112353a5a1b3Sopenharmony_ci
112453a5a1b3Sopenharmony_ci            idx += 1;
112553a5a1b3Sopenharmony_ci            connection_write(c, &idx, sizeof(uint32_t));
112653a5a1b3Sopenharmony_ci        }
112753a5a1b3Sopenharmony_ci
112853a5a1b3Sopenharmony_ci    } else if (c->state == ESD_STREAMING_DATA && c->sink_input) {
112953a5a1b3Sopenharmony_ci        pa_memchunk chunk;
113053a5a1b3Sopenharmony_ci        ssize_t r;
113153a5a1b3Sopenharmony_ci        size_t l;
113253a5a1b3Sopenharmony_ci        void *p;
113353a5a1b3Sopenharmony_ci        size_t space = 0;
113453a5a1b3Sopenharmony_ci
113553a5a1b3Sopenharmony_ci        pa_assert(c->input_memblockq);
113653a5a1b3Sopenharmony_ci
113753a5a1b3Sopenharmony_ci/*         pa_log("STREAMING_DATA"); */
113853a5a1b3Sopenharmony_ci
113953a5a1b3Sopenharmony_ci        if ((l = (size_t) pa_atomic_load(&c->playback.missing)) <= 0)
114053a5a1b3Sopenharmony_ci            return 0;
114153a5a1b3Sopenharmony_ci
114253a5a1b3Sopenharmony_ci        if (c->playback.current_memblock) {
114353a5a1b3Sopenharmony_ci
114453a5a1b3Sopenharmony_ci            space = pa_memblock_get_length(c->playback.current_memblock) - c->playback.memblock_index;
114553a5a1b3Sopenharmony_ci
114653a5a1b3Sopenharmony_ci            if (space <= 0) {
114753a5a1b3Sopenharmony_ci                pa_memblock_unref(c->playback.current_memblock);
114853a5a1b3Sopenharmony_ci                c->playback.current_memblock = NULL;
114953a5a1b3Sopenharmony_ci            }
115053a5a1b3Sopenharmony_ci        }
115153a5a1b3Sopenharmony_ci
115253a5a1b3Sopenharmony_ci        if (!c->playback.current_memblock) {
115353a5a1b3Sopenharmony_ci            pa_assert_se(c->playback.current_memblock = pa_memblock_new(c->protocol->core->mempool, (size_t) -1));
115453a5a1b3Sopenharmony_ci            c->playback.memblock_index = 0;
115553a5a1b3Sopenharmony_ci
115653a5a1b3Sopenharmony_ci            space = pa_memblock_get_length(c->playback.current_memblock);
115753a5a1b3Sopenharmony_ci        }
115853a5a1b3Sopenharmony_ci
115953a5a1b3Sopenharmony_ci        if (l > space)
116053a5a1b3Sopenharmony_ci            l = space;
116153a5a1b3Sopenharmony_ci
116253a5a1b3Sopenharmony_ci        p = pa_memblock_acquire(c->playback.current_memblock);
116353a5a1b3Sopenharmony_ci        r = pa_iochannel_read(c->io, (uint8_t*) p+c->playback.memblock_index, l);
116453a5a1b3Sopenharmony_ci        pa_memblock_release(c->playback.current_memblock);
116553a5a1b3Sopenharmony_ci
116653a5a1b3Sopenharmony_ci        if (r <= 0) {
116753a5a1b3Sopenharmony_ci
116853a5a1b3Sopenharmony_ci            if (r < 0 && errno == EAGAIN)
116953a5a1b3Sopenharmony_ci                return 0;
117053a5a1b3Sopenharmony_ci
117153a5a1b3Sopenharmony_ci            pa_log_debug("read(): %s", r < 0 ? pa_cstrerror(errno) : "EOF");
117253a5a1b3Sopenharmony_ci            return -1;
117353a5a1b3Sopenharmony_ci        }
117453a5a1b3Sopenharmony_ci
117553a5a1b3Sopenharmony_ci        chunk.memblock = c->playback.current_memblock;
117653a5a1b3Sopenharmony_ci        chunk.index = c->playback.memblock_index;
117753a5a1b3Sopenharmony_ci        chunk.length = (size_t) r;
117853a5a1b3Sopenharmony_ci
117953a5a1b3Sopenharmony_ci        c->playback.memblock_index += (size_t) r;
118053a5a1b3Sopenharmony_ci
118153a5a1b3Sopenharmony_ci        pa_atomic_sub(&c->playback.missing, (int) r);
118253a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_POST_DATA, NULL, 0, &chunk, NULL);
118353a5a1b3Sopenharmony_ci    }
118453a5a1b3Sopenharmony_ci
118553a5a1b3Sopenharmony_ci    return 0;
118653a5a1b3Sopenharmony_ci}
118753a5a1b3Sopenharmony_ci
118853a5a1b3Sopenharmony_cistatic int do_write(connection *c) {
118953a5a1b3Sopenharmony_ci    connection_assert_ref(c);
119053a5a1b3Sopenharmony_ci
119153a5a1b3Sopenharmony_ci/*     pa_log("WRITE"); */
119253a5a1b3Sopenharmony_ci
119353a5a1b3Sopenharmony_ci    if (c->write_data_length) {
119453a5a1b3Sopenharmony_ci        ssize_t r;
119553a5a1b3Sopenharmony_ci
119653a5a1b3Sopenharmony_ci        pa_assert(c->write_data_index < c->write_data_length);
119753a5a1b3Sopenharmony_ci        if ((r = pa_iochannel_write(c->io, (uint8_t*) c->write_data+c->write_data_index, c->write_data_length-c->write_data_index)) < 0) {
119853a5a1b3Sopenharmony_ci            pa_log("write(): %s", pa_cstrerror(errno));
119953a5a1b3Sopenharmony_ci            return -1;
120053a5a1b3Sopenharmony_ci        }
120153a5a1b3Sopenharmony_ci
120253a5a1b3Sopenharmony_ci        c->write_data_index += (size_t) r;
120353a5a1b3Sopenharmony_ci        if (c->write_data_index >= c->write_data_length)
120453a5a1b3Sopenharmony_ci            c->write_data_length = c->write_data_index = 0;
120553a5a1b3Sopenharmony_ci
120653a5a1b3Sopenharmony_ci        return 1;
120753a5a1b3Sopenharmony_ci
120853a5a1b3Sopenharmony_ci    } else if (c->state == ESD_STREAMING_DATA && c->source_output) {
120953a5a1b3Sopenharmony_ci        pa_memchunk chunk;
121053a5a1b3Sopenharmony_ci        ssize_t r;
121153a5a1b3Sopenharmony_ci        void *p;
121253a5a1b3Sopenharmony_ci
121353a5a1b3Sopenharmony_ci        if (pa_memblockq_peek(c->output_memblockq, &chunk) < 0)
121453a5a1b3Sopenharmony_ci            return 0;
121553a5a1b3Sopenharmony_ci
121653a5a1b3Sopenharmony_ci        pa_assert(chunk.memblock);
121753a5a1b3Sopenharmony_ci        pa_assert(chunk.length);
121853a5a1b3Sopenharmony_ci
121953a5a1b3Sopenharmony_ci        p = pa_memblock_acquire(chunk.memblock);
122053a5a1b3Sopenharmony_ci        r = pa_iochannel_write(c->io, (uint8_t*) p+chunk.index, chunk.length);
122153a5a1b3Sopenharmony_ci        pa_memblock_release(chunk.memblock);
122253a5a1b3Sopenharmony_ci
122353a5a1b3Sopenharmony_ci        pa_memblock_unref(chunk.memblock);
122453a5a1b3Sopenharmony_ci
122553a5a1b3Sopenharmony_ci        if (r < 0) {
122653a5a1b3Sopenharmony_ci            pa_log("write(): %s", pa_cstrerror(errno));
122753a5a1b3Sopenharmony_ci            return -1;
122853a5a1b3Sopenharmony_ci        }
122953a5a1b3Sopenharmony_ci
123053a5a1b3Sopenharmony_ci        pa_memblockq_drop(c->output_memblockq, (size_t) r);
123153a5a1b3Sopenharmony_ci        return 1;
123253a5a1b3Sopenharmony_ci    }
123353a5a1b3Sopenharmony_ci
123453a5a1b3Sopenharmony_ci    return 0;
123553a5a1b3Sopenharmony_ci}
123653a5a1b3Sopenharmony_ci
123753a5a1b3Sopenharmony_cistatic void do_work(connection *c) {
123853a5a1b3Sopenharmony_ci    connection_assert_ref(c);
123953a5a1b3Sopenharmony_ci
124053a5a1b3Sopenharmony_ci    c->protocol->core->mainloop->defer_enable(c->defer_event, 0);
124153a5a1b3Sopenharmony_ci
124253a5a1b3Sopenharmony_ci    if (c->dead)
124353a5a1b3Sopenharmony_ci        return;
124453a5a1b3Sopenharmony_ci
124553a5a1b3Sopenharmony_ci    if (pa_iochannel_is_readable(c->io))
124653a5a1b3Sopenharmony_ci        if (do_read(c) < 0)
124753a5a1b3Sopenharmony_ci            goto fail;
124853a5a1b3Sopenharmony_ci
124953a5a1b3Sopenharmony_ci    if (c->state == ESD_STREAMING_DATA && !c->sink_input && pa_iochannel_is_hungup(c->io))
125053a5a1b3Sopenharmony_ci        /* In case we are in capture mode we will never call read()
125153a5a1b3Sopenharmony_ci         * on the socket, hence we need to detect the hangup manually
125253a5a1b3Sopenharmony_ci         * here, instead of simply waiting for read() to return 0. */
125353a5a1b3Sopenharmony_ci        goto fail;
125453a5a1b3Sopenharmony_ci
125553a5a1b3Sopenharmony_ci    while (pa_iochannel_is_writable(c->io)) {
125653a5a1b3Sopenharmony_ci        int r = do_write(c);
125753a5a1b3Sopenharmony_ci        if (r < 0)
125853a5a1b3Sopenharmony_ci            goto fail;
125953a5a1b3Sopenharmony_ci        if (r == 0)
126053a5a1b3Sopenharmony_ci            break;
126153a5a1b3Sopenharmony_ci    }
126253a5a1b3Sopenharmony_ci
126353a5a1b3Sopenharmony_ci    return;
126453a5a1b3Sopenharmony_ci
126553a5a1b3Sopenharmony_cifail:
126653a5a1b3Sopenharmony_ci
126753a5a1b3Sopenharmony_ci    if (c->state == ESD_STREAMING_DATA && c->sink_input) {
126853a5a1b3Sopenharmony_ci        c->dead = true;
126953a5a1b3Sopenharmony_ci
127053a5a1b3Sopenharmony_ci        pa_iochannel_free(c->io);
127153a5a1b3Sopenharmony_ci        c->io = NULL;
127253a5a1b3Sopenharmony_ci
127353a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(c->sink_input->sink->asyncmsgq, PA_MSGOBJECT(c->sink_input), SINK_INPUT_MESSAGE_DISABLE_PREBUF, NULL, 0, NULL, NULL);
127453a5a1b3Sopenharmony_ci    } else
127553a5a1b3Sopenharmony_ci        connection_unlink(c);
127653a5a1b3Sopenharmony_ci}
127753a5a1b3Sopenharmony_ci
127853a5a1b3Sopenharmony_cistatic void io_callback(pa_iochannel*io, void *userdata) {
127953a5a1b3Sopenharmony_ci    connection *c = CONNECTION(userdata);
128053a5a1b3Sopenharmony_ci
128153a5a1b3Sopenharmony_ci    connection_assert_ref(c);
128253a5a1b3Sopenharmony_ci    pa_assert(io);
128353a5a1b3Sopenharmony_ci
128453a5a1b3Sopenharmony_ci    do_work(c);
128553a5a1b3Sopenharmony_ci}
128653a5a1b3Sopenharmony_ci
128753a5a1b3Sopenharmony_cistatic void defer_callback(pa_mainloop_api*a, pa_defer_event *e, void *userdata) {
128853a5a1b3Sopenharmony_ci    connection *c = CONNECTION(userdata);
128953a5a1b3Sopenharmony_ci
129053a5a1b3Sopenharmony_ci    connection_assert_ref(c);
129153a5a1b3Sopenharmony_ci    pa_assert(e);
129253a5a1b3Sopenharmony_ci
129353a5a1b3Sopenharmony_ci    do_work(c);
129453a5a1b3Sopenharmony_ci}
129553a5a1b3Sopenharmony_ci
129653a5a1b3Sopenharmony_cistatic int connection_process_msg(pa_msgobject *o, int code, void*userdata, int64_t offset, pa_memchunk *chunk) {
129753a5a1b3Sopenharmony_ci    connection *c = CONNECTION(o);
129853a5a1b3Sopenharmony_ci    connection_assert_ref(c);
129953a5a1b3Sopenharmony_ci
130053a5a1b3Sopenharmony_ci    if (!c->protocol)
130153a5a1b3Sopenharmony_ci        return -1;
130253a5a1b3Sopenharmony_ci
130353a5a1b3Sopenharmony_ci    switch (code) {
130453a5a1b3Sopenharmony_ci        case CONNECTION_MESSAGE_REQUEST_DATA:
130553a5a1b3Sopenharmony_ci            do_work(c);
130653a5a1b3Sopenharmony_ci            break;
130753a5a1b3Sopenharmony_ci
130853a5a1b3Sopenharmony_ci        case CONNECTION_MESSAGE_POST_DATA:
130953a5a1b3Sopenharmony_ci/*             pa_log("got data %u", chunk->length); */
131053a5a1b3Sopenharmony_ci            pa_memblockq_push_align(c->output_memblockq, chunk);
131153a5a1b3Sopenharmony_ci            do_work(c);
131253a5a1b3Sopenharmony_ci            break;
131353a5a1b3Sopenharmony_ci
131453a5a1b3Sopenharmony_ci        case CONNECTION_MESSAGE_UNLINK_CONNECTION:
131553a5a1b3Sopenharmony_ci            connection_unlink(c);
131653a5a1b3Sopenharmony_ci            break;
131753a5a1b3Sopenharmony_ci    }
131853a5a1b3Sopenharmony_ci
131953a5a1b3Sopenharmony_ci    return 0;
132053a5a1b3Sopenharmony_ci}
132153a5a1b3Sopenharmony_ci
132253a5a1b3Sopenharmony_ci/*** sink_input callbacks ***/
132353a5a1b3Sopenharmony_ci
132453a5a1b3Sopenharmony_ci/* Called from thread context */
132553a5a1b3Sopenharmony_cistatic int sink_input_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
132653a5a1b3Sopenharmony_ci    pa_sink_input *i = PA_SINK_INPUT(o);
132753a5a1b3Sopenharmony_ci    connection*c;
132853a5a1b3Sopenharmony_ci
132953a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
133053a5a1b3Sopenharmony_ci    c = CONNECTION(i->userdata);
133153a5a1b3Sopenharmony_ci    connection_assert_ref(c);
133253a5a1b3Sopenharmony_ci
133353a5a1b3Sopenharmony_ci    switch (code) {
133453a5a1b3Sopenharmony_ci
133553a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_POST_DATA: {
133653a5a1b3Sopenharmony_ci            pa_assert(chunk);
133753a5a1b3Sopenharmony_ci
133853a5a1b3Sopenharmony_ci            /* New data from the main loop */
133953a5a1b3Sopenharmony_ci            pa_memblockq_push_align(c->input_memblockq, chunk);
134053a5a1b3Sopenharmony_ci
134153a5a1b3Sopenharmony_ci            if (pa_memblockq_is_readable(c->input_memblockq) && c->playback.underrun) {
134253a5a1b3Sopenharmony_ci                pa_log_debug("Requesting rewind due to end of underrun.");
134353a5a1b3Sopenharmony_ci                pa_sink_input_request_rewind(c->sink_input, 0, false, true, false);
134453a5a1b3Sopenharmony_ci            }
134553a5a1b3Sopenharmony_ci
134653a5a1b3Sopenharmony_ci/*             pa_log("got data, %u", pa_memblockq_get_length(c->input_memblockq)); */
134753a5a1b3Sopenharmony_ci
134853a5a1b3Sopenharmony_ci            return 0;
134953a5a1b3Sopenharmony_ci        }
135053a5a1b3Sopenharmony_ci
135153a5a1b3Sopenharmony_ci        case SINK_INPUT_MESSAGE_DISABLE_PREBUF:
135253a5a1b3Sopenharmony_ci            pa_memblockq_prebuf_disable(c->input_memblockq);
135353a5a1b3Sopenharmony_ci            return 0;
135453a5a1b3Sopenharmony_ci
135553a5a1b3Sopenharmony_ci        case PA_SINK_INPUT_MESSAGE_GET_LATENCY: {
135653a5a1b3Sopenharmony_ci            pa_usec_t *r = userdata;
135753a5a1b3Sopenharmony_ci
135853a5a1b3Sopenharmony_ci            /* The default handler will add in the extra latency added by the resampler. */
135953a5a1b3Sopenharmony_ci            *r = pa_bytes_to_usec(pa_memblockq_get_length(c->input_memblockq), &c->sink_input->sample_spec);
136053a5a1b3Sopenharmony_ci        }
136153a5a1b3Sopenharmony_ci        /* Fall through. */
136253a5a1b3Sopenharmony_ci
136353a5a1b3Sopenharmony_ci        default:
136453a5a1b3Sopenharmony_ci            return pa_sink_input_process_msg(o, code, userdata, offset, chunk);
136553a5a1b3Sopenharmony_ci    }
136653a5a1b3Sopenharmony_ci}
136753a5a1b3Sopenharmony_ci
136853a5a1b3Sopenharmony_ci/* Called from thread context */
136953a5a1b3Sopenharmony_cistatic int sink_input_pop_cb(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
137053a5a1b3Sopenharmony_ci    connection*c;
137153a5a1b3Sopenharmony_ci
137253a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
137353a5a1b3Sopenharmony_ci    c = CONNECTION(i->userdata);
137453a5a1b3Sopenharmony_ci    connection_assert_ref(c);
137553a5a1b3Sopenharmony_ci    pa_assert(chunk);
137653a5a1b3Sopenharmony_ci
137753a5a1b3Sopenharmony_ci    if (pa_memblockq_peek(c->input_memblockq, chunk) < 0) {
137853a5a1b3Sopenharmony_ci
137953a5a1b3Sopenharmony_ci        c->playback.underrun = true;
138053a5a1b3Sopenharmony_ci
138153a5a1b3Sopenharmony_ci        if (c->dead && pa_sink_input_safe_to_remove(i))
138253a5a1b3Sopenharmony_ci            pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_UNLINK_CONNECTION, NULL, 0, NULL, NULL);
138353a5a1b3Sopenharmony_ci
138453a5a1b3Sopenharmony_ci        return -1;
138553a5a1b3Sopenharmony_ci    } else {
138653a5a1b3Sopenharmony_ci        size_t m;
138753a5a1b3Sopenharmony_ci
138853a5a1b3Sopenharmony_ci        c->playback.underrun = false;
138953a5a1b3Sopenharmony_ci
139053a5a1b3Sopenharmony_ci        chunk->length = PA_MIN(length, chunk->length);
139153a5a1b3Sopenharmony_ci        pa_memblockq_drop(c->input_memblockq, chunk->length);
139253a5a1b3Sopenharmony_ci        m = pa_memblockq_pop_missing(c->input_memblockq);
139353a5a1b3Sopenharmony_ci
139453a5a1b3Sopenharmony_ci        if (m > 0)
139553a5a1b3Sopenharmony_ci            if (pa_atomic_add(&c->playback.missing, (int) m) <= 0)
139653a5a1b3Sopenharmony_ci                pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_REQUEST_DATA, NULL, 0, NULL, NULL);
139753a5a1b3Sopenharmony_ci
139853a5a1b3Sopenharmony_ci        return 0;
139953a5a1b3Sopenharmony_ci    }
140053a5a1b3Sopenharmony_ci}
140153a5a1b3Sopenharmony_ci
140253a5a1b3Sopenharmony_ci/* Called from thread context */
140353a5a1b3Sopenharmony_cistatic void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) {
140453a5a1b3Sopenharmony_ci    connection *c;
140553a5a1b3Sopenharmony_ci
140653a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
140753a5a1b3Sopenharmony_ci    c = CONNECTION(i->userdata);
140853a5a1b3Sopenharmony_ci    connection_assert_ref(c);
140953a5a1b3Sopenharmony_ci
141053a5a1b3Sopenharmony_ci    /* If we are in an underrun, then we don't rewind */
141153a5a1b3Sopenharmony_ci    if (i->thread_info.underrun_for > 0)
141253a5a1b3Sopenharmony_ci        return;
141353a5a1b3Sopenharmony_ci
141453a5a1b3Sopenharmony_ci    pa_memblockq_rewind(c->input_memblockq, nbytes);
141553a5a1b3Sopenharmony_ci}
141653a5a1b3Sopenharmony_ci
141753a5a1b3Sopenharmony_ci/* Called from thread context */
141853a5a1b3Sopenharmony_cistatic void sink_input_update_max_rewind_cb(pa_sink_input *i, size_t nbytes) {
141953a5a1b3Sopenharmony_ci    connection *c;
142053a5a1b3Sopenharmony_ci
142153a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
142253a5a1b3Sopenharmony_ci    c = CONNECTION(i->userdata);
142353a5a1b3Sopenharmony_ci    connection_assert_ref(c);
142453a5a1b3Sopenharmony_ci
142553a5a1b3Sopenharmony_ci    pa_memblockq_set_maxrewind(c->input_memblockq, nbytes);
142653a5a1b3Sopenharmony_ci}
142753a5a1b3Sopenharmony_ci
142853a5a1b3Sopenharmony_cistatic void sink_input_kill_cb(pa_sink_input *i) {
142953a5a1b3Sopenharmony_ci    pa_sink_input_assert_ref(i);
143053a5a1b3Sopenharmony_ci
143153a5a1b3Sopenharmony_ci    connection_unlink(CONNECTION(i->userdata));
143253a5a1b3Sopenharmony_ci}
143353a5a1b3Sopenharmony_ci
143453a5a1b3Sopenharmony_ci/*** source_output callbacks ***/
143553a5a1b3Sopenharmony_ci
143653a5a1b3Sopenharmony_ci/* Called from thread context */
143753a5a1b3Sopenharmony_cistatic void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) {
143853a5a1b3Sopenharmony_ci    connection *c;
143953a5a1b3Sopenharmony_ci
144053a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
144153a5a1b3Sopenharmony_ci    c = CONNECTION(o->userdata);
144253a5a1b3Sopenharmony_ci    pa_assert(c);
144353a5a1b3Sopenharmony_ci    pa_assert(chunk);
144453a5a1b3Sopenharmony_ci
144553a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(c), CONNECTION_MESSAGE_POST_DATA, NULL, 0, chunk, NULL);
144653a5a1b3Sopenharmony_ci}
144753a5a1b3Sopenharmony_ci
144853a5a1b3Sopenharmony_cistatic void source_output_kill_cb(pa_source_output *o) {
144953a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
145053a5a1b3Sopenharmony_ci
145153a5a1b3Sopenharmony_ci    connection_unlink(CONNECTION(o->userdata));
145253a5a1b3Sopenharmony_ci}
145353a5a1b3Sopenharmony_ci
145453a5a1b3Sopenharmony_cistatic pa_usec_t source_output_get_latency_cb(pa_source_output *o) {
145553a5a1b3Sopenharmony_ci    connection*c;
145653a5a1b3Sopenharmony_ci
145753a5a1b3Sopenharmony_ci    pa_source_output_assert_ref(o);
145853a5a1b3Sopenharmony_ci    c = CONNECTION(o->userdata);
145953a5a1b3Sopenharmony_ci    pa_assert(c);
146053a5a1b3Sopenharmony_ci
146153a5a1b3Sopenharmony_ci    return pa_bytes_to_usec(pa_memblockq_get_length(c->output_memblockq), &c->source_output->sample_spec);
146253a5a1b3Sopenharmony_ci}
146353a5a1b3Sopenharmony_ci
146453a5a1b3Sopenharmony_ci/*** entry points ***/
146553a5a1b3Sopenharmony_ci
146653a5a1b3Sopenharmony_cistatic void auth_timeout(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
146753a5a1b3Sopenharmony_ci    connection *c = CONNECTION(userdata);
146853a5a1b3Sopenharmony_ci
146953a5a1b3Sopenharmony_ci    pa_assert(m);
147053a5a1b3Sopenharmony_ci    connection_assert_ref(c);
147153a5a1b3Sopenharmony_ci    pa_assert(c->auth_timeout_event == e);
147253a5a1b3Sopenharmony_ci
147353a5a1b3Sopenharmony_ci    if (!c->authorized)
147453a5a1b3Sopenharmony_ci        connection_unlink(c);
147553a5a1b3Sopenharmony_ci}
147653a5a1b3Sopenharmony_ci
147753a5a1b3Sopenharmony_civoid pa_esound_protocol_connect(pa_esound_protocol *p, pa_iochannel *io, pa_esound_options *o) {
147853a5a1b3Sopenharmony_ci    connection *c;
147953a5a1b3Sopenharmony_ci    char pname[128];
148053a5a1b3Sopenharmony_ci    pa_client_new_data data;
148153a5a1b3Sopenharmony_ci    pa_client *client;
148253a5a1b3Sopenharmony_ci
148353a5a1b3Sopenharmony_ci    pa_assert(p);
148453a5a1b3Sopenharmony_ci    pa_assert(io);
148553a5a1b3Sopenharmony_ci    pa_assert(o);
148653a5a1b3Sopenharmony_ci
148753a5a1b3Sopenharmony_ci    if (pa_idxset_size(p->connections)+1 > MAX_CONNECTIONS) {
148853a5a1b3Sopenharmony_ci        pa_log("Warning! Too many connections (%u), dropping incoming connection.", MAX_CONNECTIONS);
148953a5a1b3Sopenharmony_ci        pa_iochannel_free(io);
149053a5a1b3Sopenharmony_ci        return;
149153a5a1b3Sopenharmony_ci    }
149253a5a1b3Sopenharmony_ci
149353a5a1b3Sopenharmony_ci    pa_client_new_data_init(&data);
149453a5a1b3Sopenharmony_ci    data.module = o->module;
149553a5a1b3Sopenharmony_ci    data.driver = __FILE__;
149653a5a1b3Sopenharmony_ci    pa_iochannel_socket_peer_to_string(io, pname, sizeof(pname));
149753a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "EsounD client (%s)", pname);
149853a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "esound-protocol.peer", pname);
149953a5a1b3Sopenharmony_ci    client = pa_client_new(p->core, &data);
150053a5a1b3Sopenharmony_ci    pa_client_new_data_done(&data);
150153a5a1b3Sopenharmony_ci
150253a5a1b3Sopenharmony_ci    if (!client)
150353a5a1b3Sopenharmony_ci        return;
150453a5a1b3Sopenharmony_ci
150553a5a1b3Sopenharmony_ci    c = pa_msgobject_new(connection);
150653a5a1b3Sopenharmony_ci    c->parent.parent.free = connection_free;
150753a5a1b3Sopenharmony_ci    c->parent.process_msg = connection_process_msg;
150853a5a1b3Sopenharmony_ci    c->protocol = p;
150953a5a1b3Sopenharmony_ci    c->io = io;
151053a5a1b3Sopenharmony_ci    pa_iochannel_set_callback(c->io, io_callback, c);
151153a5a1b3Sopenharmony_ci
151253a5a1b3Sopenharmony_ci    c->client = client;
151353a5a1b3Sopenharmony_ci    c->client->kill = client_kill_cb;
151453a5a1b3Sopenharmony_ci    c->client->userdata = c;
151553a5a1b3Sopenharmony_ci
151653a5a1b3Sopenharmony_ci    c->options = pa_esound_options_ref(o);
151753a5a1b3Sopenharmony_ci    c->authorized = false;
151853a5a1b3Sopenharmony_ci    c->swap_byte_order = false;
151953a5a1b3Sopenharmony_ci    c->dead = false;
152053a5a1b3Sopenharmony_ci
152153a5a1b3Sopenharmony_ci    c->read_data_length = 0;
152253a5a1b3Sopenharmony_ci    c->read_data = pa_xmalloc(c->read_data_alloc = proto_map[ESD_PROTO_CONNECT].data_length);
152353a5a1b3Sopenharmony_ci
152453a5a1b3Sopenharmony_ci    c->write_data_length = c->write_data_index = c->write_data_alloc = 0;
152553a5a1b3Sopenharmony_ci    c->write_data = NULL;
152653a5a1b3Sopenharmony_ci
152753a5a1b3Sopenharmony_ci    c->state = ESD_NEEDS_REQDATA;
152853a5a1b3Sopenharmony_ci    c->request = ESD_PROTO_CONNECT;
152953a5a1b3Sopenharmony_ci
153053a5a1b3Sopenharmony_ci    c->sink_input = NULL;
153153a5a1b3Sopenharmony_ci    c->input_memblockq = NULL;
153253a5a1b3Sopenharmony_ci
153353a5a1b3Sopenharmony_ci    c->source_output = NULL;
153453a5a1b3Sopenharmony_ci    c->output_memblockq = NULL;
153553a5a1b3Sopenharmony_ci
153653a5a1b3Sopenharmony_ci    c->playback.current_memblock = NULL;
153753a5a1b3Sopenharmony_ci    c->playback.memblock_index = 0;
153853a5a1b3Sopenharmony_ci    c->playback.underrun = true;
153953a5a1b3Sopenharmony_ci    pa_atomic_store(&c->playback.missing, 0);
154053a5a1b3Sopenharmony_ci
154153a5a1b3Sopenharmony_ci    pa_memchunk_reset(&c->scache.memchunk);
154253a5a1b3Sopenharmony_ci    c->scache.name = NULL;
154353a5a1b3Sopenharmony_ci
154453a5a1b3Sopenharmony_ci    c->original_name = NULL;
154553a5a1b3Sopenharmony_ci
154653a5a1b3Sopenharmony_ci    if (o->auth_anonymous) {
154753a5a1b3Sopenharmony_ci        pa_log_info("Client authenticated anonymously.");
154853a5a1b3Sopenharmony_ci        c->authorized = true;
154953a5a1b3Sopenharmony_ci    }
155053a5a1b3Sopenharmony_ci
155153a5a1b3Sopenharmony_ci    if (!c->authorized &&
155253a5a1b3Sopenharmony_ci        o->auth_ip_acl &&
155353a5a1b3Sopenharmony_ci        pa_ip_acl_check(o->auth_ip_acl, pa_iochannel_get_recv_fd(io)) > 0) {
155453a5a1b3Sopenharmony_ci
155553a5a1b3Sopenharmony_ci        pa_log_info("Client authenticated by IP ACL.");
155653a5a1b3Sopenharmony_ci        c->authorized = true;
155753a5a1b3Sopenharmony_ci    }
155853a5a1b3Sopenharmony_ci
155953a5a1b3Sopenharmony_ci    if (!c->authorized)
156053a5a1b3Sopenharmony_ci        c->auth_timeout_event = pa_core_rttime_new(p->core, pa_rtclock_now() + AUTH_TIMEOUT, auth_timeout, c);
156153a5a1b3Sopenharmony_ci    else
156253a5a1b3Sopenharmony_ci        c->auth_timeout_event = NULL;
156353a5a1b3Sopenharmony_ci
156453a5a1b3Sopenharmony_ci    c->defer_event = p->core->mainloop->defer_new(p->core->mainloop, defer_callback, c);
156553a5a1b3Sopenharmony_ci    p->core->mainloop->defer_enable(c->defer_event, 0);
156653a5a1b3Sopenharmony_ci
156753a5a1b3Sopenharmony_ci    pa_idxset_put(p->connections, c, &c->index);
156853a5a1b3Sopenharmony_ci}
156953a5a1b3Sopenharmony_ci
157053a5a1b3Sopenharmony_civoid pa_esound_protocol_disconnect(pa_esound_protocol *p, pa_module *m) {
157153a5a1b3Sopenharmony_ci    connection *c;
157253a5a1b3Sopenharmony_ci    void *state = NULL;
157353a5a1b3Sopenharmony_ci
157453a5a1b3Sopenharmony_ci    pa_assert(p);
157553a5a1b3Sopenharmony_ci    pa_assert(m);
157653a5a1b3Sopenharmony_ci
157753a5a1b3Sopenharmony_ci    while ((c = pa_idxset_iterate(p->connections, &state, NULL)))
157853a5a1b3Sopenharmony_ci        if (c->options->module == m)
157953a5a1b3Sopenharmony_ci            connection_unlink(c);
158053a5a1b3Sopenharmony_ci}
158153a5a1b3Sopenharmony_ci
158253a5a1b3Sopenharmony_cistatic pa_esound_protocol* esound_protocol_new(pa_core *c) {
158353a5a1b3Sopenharmony_ci    pa_esound_protocol *p;
158453a5a1b3Sopenharmony_ci
158553a5a1b3Sopenharmony_ci    pa_assert(c);
158653a5a1b3Sopenharmony_ci
158753a5a1b3Sopenharmony_ci    p = pa_xnew(pa_esound_protocol, 1);
158853a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(p);
158953a5a1b3Sopenharmony_ci    p->core = c;
159053a5a1b3Sopenharmony_ci    p->connections = pa_idxset_new(NULL, NULL);
159153a5a1b3Sopenharmony_ci    p->n_player = 0;
159253a5a1b3Sopenharmony_ci
159353a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_set(c, "esound-protocol", p) >= 0);
159453a5a1b3Sopenharmony_ci
159553a5a1b3Sopenharmony_ci    return p;
159653a5a1b3Sopenharmony_ci}
159753a5a1b3Sopenharmony_ci
159853a5a1b3Sopenharmony_cipa_esound_protocol* pa_esound_protocol_get(pa_core *c) {
159953a5a1b3Sopenharmony_ci    pa_esound_protocol *p;
160053a5a1b3Sopenharmony_ci
160153a5a1b3Sopenharmony_ci    if ((p = pa_shared_get(c, "esound-protocol")))
160253a5a1b3Sopenharmony_ci        return pa_esound_protocol_ref(p);
160353a5a1b3Sopenharmony_ci
160453a5a1b3Sopenharmony_ci    return esound_protocol_new(c);
160553a5a1b3Sopenharmony_ci}
160653a5a1b3Sopenharmony_ci
160753a5a1b3Sopenharmony_cipa_esound_protocol* pa_esound_protocol_ref(pa_esound_protocol *p) {
160853a5a1b3Sopenharmony_ci    pa_assert(p);
160953a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
161053a5a1b3Sopenharmony_ci
161153a5a1b3Sopenharmony_ci    PA_REFCNT_INC(p);
161253a5a1b3Sopenharmony_ci
161353a5a1b3Sopenharmony_ci    return p;
161453a5a1b3Sopenharmony_ci}
161553a5a1b3Sopenharmony_ci
161653a5a1b3Sopenharmony_civoid pa_esound_protocol_unref(pa_esound_protocol *p) {
161753a5a1b3Sopenharmony_ci    connection *c;
161853a5a1b3Sopenharmony_ci    pa_assert(p);
161953a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(p) >= 1);
162053a5a1b3Sopenharmony_ci
162153a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(p) > 0)
162253a5a1b3Sopenharmony_ci        return;
162353a5a1b3Sopenharmony_ci
162453a5a1b3Sopenharmony_ci    while ((c = pa_idxset_first(p->connections, NULL)))
162553a5a1b3Sopenharmony_ci        connection_unlink(c);
162653a5a1b3Sopenharmony_ci
162753a5a1b3Sopenharmony_ci    pa_idxset_free(p->connections, NULL);
162853a5a1b3Sopenharmony_ci
162953a5a1b3Sopenharmony_ci    pa_assert_se(pa_shared_remove(p->core, "esound-protocol") >= 0);
163053a5a1b3Sopenharmony_ci
163153a5a1b3Sopenharmony_ci    pa_xfree(p);
163253a5a1b3Sopenharmony_ci}
163353a5a1b3Sopenharmony_ci
163453a5a1b3Sopenharmony_cipa_esound_options* pa_esound_options_new(void) {
163553a5a1b3Sopenharmony_ci    pa_esound_options *o;
163653a5a1b3Sopenharmony_ci
163753a5a1b3Sopenharmony_ci    o = pa_xnew0(pa_esound_options, 1);
163853a5a1b3Sopenharmony_ci    PA_REFCNT_INIT(o);
163953a5a1b3Sopenharmony_ci
164053a5a1b3Sopenharmony_ci    return o;
164153a5a1b3Sopenharmony_ci}
164253a5a1b3Sopenharmony_ci
164353a5a1b3Sopenharmony_cipa_esound_options* pa_esound_options_ref(pa_esound_options *o) {
164453a5a1b3Sopenharmony_ci    pa_assert(o);
164553a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(o) >= 1);
164653a5a1b3Sopenharmony_ci
164753a5a1b3Sopenharmony_ci    PA_REFCNT_INC(o);
164853a5a1b3Sopenharmony_ci
164953a5a1b3Sopenharmony_ci    return o;
165053a5a1b3Sopenharmony_ci}
165153a5a1b3Sopenharmony_ci
165253a5a1b3Sopenharmony_civoid pa_esound_options_unref(pa_esound_options *o) {
165353a5a1b3Sopenharmony_ci    pa_assert(o);
165453a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(o) >= 1);
165553a5a1b3Sopenharmony_ci
165653a5a1b3Sopenharmony_ci    if (PA_REFCNT_DEC(o) > 0)
165753a5a1b3Sopenharmony_ci        return;
165853a5a1b3Sopenharmony_ci
165953a5a1b3Sopenharmony_ci    if (o->auth_ip_acl)
166053a5a1b3Sopenharmony_ci        pa_ip_acl_free(o->auth_ip_acl);
166153a5a1b3Sopenharmony_ci
166253a5a1b3Sopenharmony_ci    if (o->auth_cookie)
166353a5a1b3Sopenharmony_ci        pa_auth_cookie_unref(o->auth_cookie);
166453a5a1b3Sopenharmony_ci
166553a5a1b3Sopenharmony_ci    pa_xfree(o->default_sink);
166653a5a1b3Sopenharmony_ci    pa_xfree(o->default_source);
166753a5a1b3Sopenharmony_ci
166853a5a1b3Sopenharmony_ci    pa_xfree(o);
166953a5a1b3Sopenharmony_ci}
167053a5a1b3Sopenharmony_ci
167153a5a1b3Sopenharmony_ciint pa_esound_options_parse(pa_esound_options *o, pa_core *c, pa_modargs *ma) {
167253a5a1b3Sopenharmony_ci    bool enabled;
167353a5a1b3Sopenharmony_ci    const char *acl;
167453a5a1b3Sopenharmony_ci
167553a5a1b3Sopenharmony_ci    pa_assert(o);
167653a5a1b3Sopenharmony_ci    pa_assert(PA_REFCNT_VALUE(o) >= 1);
167753a5a1b3Sopenharmony_ci    pa_assert(ma);
167853a5a1b3Sopenharmony_ci
167953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "auth-anonymous", &o->auth_anonymous) < 0) {
168053a5a1b3Sopenharmony_ci        pa_log("auth-anonymous= expects a boolean argument.");
168153a5a1b3Sopenharmony_ci        return -1;
168253a5a1b3Sopenharmony_ci    }
168353a5a1b3Sopenharmony_ci
168453a5a1b3Sopenharmony_ci    if ((acl = pa_modargs_get_value(ma, "auth-ip-acl", NULL))) {
168553a5a1b3Sopenharmony_ci        pa_ip_acl *ipa;
168653a5a1b3Sopenharmony_ci
168753a5a1b3Sopenharmony_ci        if (!(ipa = pa_ip_acl_new(acl))) {
168853a5a1b3Sopenharmony_ci            pa_log("Failed to parse IP ACL '%s'", acl);
168953a5a1b3Sopenharmony_ci            return -1;
169053a5a1b3Sopenharmony_ci        }
169153a5a1b3Sopenharmony_ci
169253a5a1b3Sopenharmony_ci        if (o->auth_ip_acl)
169353a5a1b3Sopenharmony_ci            pa_ip_acl_free(o->auth_ip_acl);
169453a5a1b3Sopenharmony_ci
169553a5a1b3Sopenharmony_ci        o->auth_ip_acl = ipa;
169653a5a1b3Sopenharmony_ci    }
169753a5a1b3Sopenharmony_ci
169853a5a1b3Sopenharmony_ci    enabled = true;
169953a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "auth-cookie-enabled", &enabled) < 0) {
170053a5a1b3Sopenharmony_ci        pa_log("auth-cookie-enabled= expects a boolean argument.");
170153a5a1b3Sopenharmony_ci        return -1;
170253a5a1b3Sopenharmony_ci    }
170353a5a1b3Sopenharmony_ci
170453a5a1b3Sopenharmony_ci    if (o->auth_cookie)
170553a5a1b3Sopenharmony_ci        pa_auth_cookie_unref(o->auth_cookie);
170653a5a1b3Sopenharmony_ci
170753a5a1b3Sopenharmony_ci    if (enabled) {
170853a5a1b3Sopenharmony_ci        char *cn;
170953a5a1b3Sopenharmony_ci
171053a5a1b3Sopenharmony_ci        /* The new name for this is 'auth-cookie', for compat reasons
171153a5a1b3Sopenharmony_ci         * we check the old name too */
171253a5a1b3Sopenharmony_ci        if (!(cn = pa_xstrdup(pa_modargs_get_value(ma, "auth-cookie", NULL)))) {
171353a5a1b3Sopenharmony_ci            if (!(cn = pa_xstrdup(pa_modargs_get_value(ma, "cookie", NULL)))) {
171453a5a1b3Sopenharmony_ci                if (pa_append_to_home_dir(DEFAULT_COOKIE_FILE, &cn) < 0)
171553a5a1b3Sopenharmony_ci                    return -1;
171653a5a1b3Sopenharmony_ci            }
171753a5a1b3Sopenharmony_ci        }
171853a5a1b3Sopenharmony_ci
171953a5a1b3Sopenharmony_ci        o->auth_cookie = pa_auth_cookie_get(c, cn, true, ESD_KEY_LEN);
172053a5a1b3Sopenharmony_ci        pa_xfree(cn);
172153a5a1b3Sopenharmony_ci        if (!o->auth_cookie)
172253a5a1b3Sopenharmony_ci            return -1;
172353a5a1b3Sopenharmony_ci
172453a5a1b3Sopenharmony_ci    } else
172553a5a1b3Sopenharmony_ci        o->auth_cookie = NULL;
172653a5a1b3Sopenharmony_ci
172753a5a1b3Sopenharmony_ci    pa_xfree(o->default_sink);
172853a5a1b3Sopenharmony_ci    o->default_sink = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
172953a5a1b3Sopenharmony_ci
173053a5a1b3Sopenharmony_ci    pa_xfree(o->default_source);
173153a5a1b3Sopenharmony_ci    o->default_source = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));
173253a5a1b3Sopenharmony_ci
173353a5a1b3Sopenharmony_ci    return 0;
173453a5a1b3Sopenharmony_ci}
1735