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 "restart-module.h"
2653a5a1b3Sopenharmony_ci
2753a5a1b3Sopenharmony_ci#include <unistd.h>
2853a5a1b3Sopenharmony_ci#include <string.h>
2953a5a1b3Sopenharmony_ci#include <errno.h>
3053a5a1b3Sopenharmony_ci#include <sys/types.h>
3153a5a1b3Sopenharmony_ci#include <stdio.h>
3253a5a1b3Sopenharmony_ci#include <stdlib.h>
3353a5a1b3Sopenharmony_ci
3453a5a1b3Sopenharmony_ci#ifdef HAVE_X11
3553a5a1b3Sopenharmony_ci#include <xcb/xcb.h>
3653a5a1b3Sopenharmony_ci#endif
3753a5a1b3Sopenharmony_ci
3853a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3953a5a1b3Sopenharmony_ci#include <pulse/timeval.h>
4053a5a1b3Sopenharmony_ci#include <pulse/util.h>
4153a5a1b3Sopenharmony_ci#include <pulse/version.h>
4253a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h>
4353a5a1b3Sopenharmony_ci
4453a5a1b3Sopenharmony_ci#include <pulsecore/module.h>
4553a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4653a5a1b3Sopenharmony_ci#include <pulsecore/modargs.h>
4753a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
4853a5a1b3Sopenharmony_ci#include <pulsecore/core-subscribe.h>
4953a5a1b3Sopenharmony_ci#include <pulsecore/pdispatch.h>
5053a5a1b3Sopenharmony_ci#include <pulsecore/pstream.h>
5153a5a1b3Sopenharmony_ci#include <pulsecore/pstream-util.h>
5253a5a1b3Sopenharmony_ci#include <pulsecore/socket-client.h>
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
5553a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother_2.h>
5653a5a1b3Sopenharmony_ci#else
5753a5a1b3Sopenharmony_ci#include <pulsecore/time-smoother.h>
5853a5a1b3Sopenharmony_ci#endif
5953a5a1b3Sopenharmony_ci
6053a5a1b3Sopenharmony_ci#include <pulsecore/thread.h>
6153a5a1b3Sopenharmony_ci#include <pulsecore/thread-mq.h>
6253a5a1b3Sopenharmony_ci#include <pulsecore/core-rtclock.h>
6353a5a1b3Sopenharmony_ci#include <pulsecore/core-error.h>
6453a5a1b3Sopenharmony_ci#include <pulsecore/proplist-util.h>
6553a5a1b3Sopenharmony_ci#include <pulsecore/auth-cookie.h>
6653a5a1b3Sopenharmony_ci#include <pulsecore/mcalign.h>
6753a5a1b3Sopenharmony_ci#include <pulsecore/strlist.h>
6853a5a1b3Sopenharmony_ci
6953a5a1b3Sopenharmony_ci#ifdef HAVE_X11
7053a5a1b3Sopenharmony_ci#include <pulsecore/x11prop.h>
7153a5a1b3Sopenharmony_ci#endif
7253a5a1b3Sopenharmony_ci
7353a5a1b3Sopenharmony_ci#define ENV_DEFAULT_SINK "PULSE_SINK"
7453a5a1b3Sopenharmony_ci#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
7553a5a1b3Sopenharmony_ci#define ENV_DEFAULT_SERVER "PULSE_SERVER"
7653a5a1b3Sopenharmony_ci#define ENV_COOKIE_FILE "PULSE_COOKIE"
7753a5a1b3Sopenharmony_ci
7853a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
7953a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Tunnel module for sinks");
8053a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
8153a5a1b3Sopenharmony_ci        "sink_name=<name for the local sink> "
8253a5a1b3Sopenharmony_ci        "sink_properties=<properties for the local sink> "
8353a5a1b3Sopenharmony_ci        "auto=<determine server/sink/cookie automatically> "
8453a5a1b3Sopenharmony_ci        "server=<address> "
8553a5a1b3Sopenharmony_ci        "sink=<remote sink name> "
8653a5a1b3Sopenharmony_ci        "reconnect_interval_ms=<interval to try reconnects, 0 or omitted if disabled> "
8753a5a1b3Sopenharmony_ci        "cookie=<filename> "
8853a5a1b3Sopenharmony_ci        "format=<sample format> "
8953a5a1b3Sopenharmony_ci        "channels=<number of channels> "
9053a5a1b3Sopenharmony_ci        "rate=<sample rate> "
9153a5a1b3Sopenharmony_ci        "latency_msec=<fixed latency in ms> "
9253a5a1b3Sopenharmony_ci        "channel_map=<channel map>");
9353a5a1b3Sopenharmony_ci#else
9453a5a1b3Sopenharmony_ciPA_MODULE_DESCRIPTION("Tunnel module for sources");
9553a5a1b3Sopenharmony_ciPA_MODULE_USAGE(
9653a5a1b3Sopenharmony_ci        "source_name=<name for the local source> "
9753a5a1b3Sopenharmony_ci        "source_properties=<properties for the local source> "
9853a5a1b3Sopenharmony_ci        "auto=<determine server/source/cookie automatically> "
9953a5a1b3Sopenharmony_ci        "server=<address> "
10053a5a1b3Sopenharmony_ci        "source=<remote source name> "
10153a5a1b3Sopenharmony_ci        "reconnect_interval_ms=<interval to try reconnects, 0 or omitted if disabled> "
10253a5a1b3Sopenharmony_ci        "cookie=<filename> "
10353a5a1b3Sopenharmony_ci        "format=<sample format> "
10453a5a1b3Sopenharmony_ci        "channels=<number of channels> "
10553a5a1b3Sopenharmony_ci        "rate=<sample rate> "
10653a5a1b3Sopenharmony_ci        "latency_msec=<fixed latency in ms> "
10753a5a1b3Sopenharmony_ci        "channel_map=<channel map>");
10853a5a1b3Sopenharmony_ci#endif
10953a5a1b3Sopenharmony_ci
11053a5a1b3Sopenharmony_ciPA_MODULE_AUTHOR("Lennart Poettering");
11153a5a1b3Sopenharmony_ciPA_MODULE_VERSION(PACKAGE_VERSION);
11253a5a1b3Sopenharmony_ciPA_MODULE_LOAD_ONCE(false);
11353a5a1b3Sopenharmony_ci
11453a5a1b3Sopenharmony_cistatic const char* const valid_modargs[] = {
11553a5a1b3Sopenharmony_ci    "auto",
11653a5a1b3Sopenharmony_ci    "server",
11753a5a1b3Sopenharmony_ci    "cookie",
11853a5a1b3Sopenharmony_ci    "format",
11953a5a1b3Sopenharmony_ci    "channels",
12053a5a1b3Sopenharmony_ci    "rate",
12153a5a1b3Sopenharmony_ci    "latency_msec",
12253a5a1b3Sopenharmony_ci    "reconnect_interval_ms",
12353a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
12453a5a1b3Sopenharmony_ci    "sink_name",
12553a5a1b3Sopenharmony_ci    "sink_properties",
12653a5a1b3Sopenharmony_ci    "sink",
12753a5a1b3Sopenharmony_ci#else
12853a5a1b3Sopenharmony_ci    "source_name",
12953a5a1b3Sopenharmony_ci    "source_properties",
13053a5a1b3Sopenharmony_ci    "source",
13153a5a1b3Sopenharmony_ci#endif
13253a5a1b3Sopenharmony_ci    "channel_map",
13353a5a1b3Sopenharmony_ci    NULL,
13453a5a1b3Sopenharmony_ci};
13553a5a1b3Sopenharmony_ci
13653a5a1b3Sopenharmony_ci#define DEFAULT_TIMEOUT 5
13753a5a1b3Sopenharmony_ci
13853a5a1b3Sopenharmony_ci#define LATENCY_INTERVAL (1*PA_USEC_PER_SEC)
13953a5a1b3Sopenharmony_ci
14053a5a1b3Sopenharmony_ci#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
14153a5a1b3Sopenharmony_ci
14253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
14353a5a1b3Sopenharmony_ci
14453a5a1b3Sopenharmony_cienum {
14553a5a1b3Sopenharmony_ci    SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
14653a5a1b3Sopenharmony_ci    SINK_MESSAGE_REMOTE_SUSPEND,
14753a5a1b3Sopenharmony_ci    SINK_MESSAGE_UPDATE_LATENCY,
14853a5a1b3Sopenharmony_ci    SINK_MESSAGE_GET_LATENCY_SNAPSHOT,
14953a5a1b3Sopenharmony_ci    SINK_MESSAGE_POST,
15053a5a1b3Sopenharmony_ci};
15153a5a1b3Sopenharmony_ci
15253a5a1b3Sopenharmony_ci#define DEFAULT_LATENCY_MSEC 100
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ci#else
15553a5a1b3Sopenharmony_ci
15653a5a1b3Sopenharmony_cienum {
15753a5a1b3Sopenharmony_ci    SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
15853a5a1b3Sopenharmony_ci    SOURCE_MESSAGE_REMOTE_SUSPEND,
15953a5a1b3Sopenharmony_ci    SOURCE_MESSAGE_UPDATE_LATENCY,
16053a5a1b3Sopenharmony_ci    SOURCE_MESSAGE_GET_LATENCY_SNAPSHOT,
16153a5a1b3Sopenharmony_ci};
16253a5a1b3Sopenharmony_ci
16353a5a1b3Sopenharmony_ci#define DEFAULT_LATENCY_MSEC 25
16453a5a1b3Sopenharmony_ci
16553a5a1b3Sopenharmony_ci#endif
16653a5a1b3Sopenharmony_ci
16753a5a1b3Sopenharmony_cistruct tunnel_msg {
16853a5a1b3Sopenharmony_ci    pa_msgobject parent;
16953a5a1b3Sopenharmony_ci};
17053a5a1b3Sopenharmony_ci
17153a5a1b3Sopenharmony_citypedef struct tunnel_msg tunnel_msg;
17253a5a1b3Sopenharmony_ciPA_DEFINE_PRIVATE_CLASS(tunnel_msg, pa_msgobject);
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_cienum {
17553a5a1b3Sopenharmony_ci    TUNNEL_MESSAGE_MAYBE_RESTART,
17653a5a1b3Sopenharmony_ci};
17753a5a1b3Sopenharmony_ci
17853a5a1b3Sopenharmony_cistatic int do_init(pa_module *m);
17953a5a1b3Sopenharmony_cistatic void do_done(pa_module *m);
18053a5a1b3Sopenharmony_ci
18153a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
18253a5a1b3Sopenharmony_cistatic void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
18353a5a1b3Sopenharmony_cistatic void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
18453a5a1b3Sopenharmony_ci#endif
18553a5a1b3Sopenharmony_cistatic void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
18653a5a1b3Sopenharmony_cistatic void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
18753a5a1b3Sopenharmony_cistatic void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
18853a5a1b3Sopenharmony_cistatic void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
18953a5a1b3Sopenharmony_cistatic void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
19053a5a1b3Sopenharmony_cistatic void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
19153a5a1b3Sopenharmony_cistatic void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
19253a5a1b3Sopenharmony_ci
19353a5a1b3Sopenharmony_cistatic const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
19453a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
19553a5a1b3Sopenharmony_ci    [PA_COMMAND_REQUEST] = command_request,
19653a5a1b3Sopenharmony_ci    [PA_COMMAND_STARTED] = command_started,
19753a5a1b3Sopenharmony_ci#endif
19853a5a1b3Sopenharmony_ci    [PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
19953a5a1b3Sopenharmony_ci    [PA_COMMAND_OVERFLOW] = command_overflow_or_underflow,
20053a5a1b3Sopenharmony_ci    [PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow,
20153a5a1b3Sopenharmony_ci    [PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
20253a5a1b3Sopenharmony_ci    [PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
20353a5a1b3Sopenharmony_ci    [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
20453a5a1b3Sopenharmony_ci    [PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
20553a5a1b3Sopenharmony_ci    [PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
20653a5a1b3Sopenharmony_ci    [PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
20753a5a1b3Sopenharmony_ci    [PA_COMMAND_PLAYBACK_STREAM_EVENT] = command_stream_or_client_event,
20853a5a1b3Sopenharmony_ci    [PA_COMMAND_RECORD_STREAM_EVENT] = command_stream_or_client_event,
20953a5a1b3Sopenharmony_ci    [PA_COMMAND_CLIENT_EVENT] = command_stream_or_client_event,
21053a5a1b3Sopenharmony_ci    [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed,
21153a5a1b3Sopenharmony_ci    [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed,
21253a5a1b3Sopenharmony_ci    [PA_COMMAND_UNDERFLOW_OHOS] = command_overflow_or_underflow,
21353a5a1b3Sopenharmony_ci};
21453a5a1b3Sopenharmony_ci
21553a5a1b3Sopenharmony_cistruct userdata {
21653a5a1b3Sopenharmony_ci    pa_core *core;
21753a5a1b3Sopenharmony_ci    pa_module *module;
21853a5a1b3Sopenharmony_ci
21953a5a1b3Sopenharmony_ci    pa_thread_mq thread_mq;
22053a5a1b3Sopenharmony_ci    pa_rtpoll *rtpoll;
22153a5a1b3Sopenharmony_ci    pa_thread *thread;
22253a5a1b3Sopenharmony_ci
22353a5a1b3Sopenharmony_ci    pa_socket_client *client;
22453a5a1b3Sopenharmony_ci    pa_pstream *pstream;
22553a5a1b3Sopenharmony_ci    pa_pdispatch *pdispatch;
22653a5a1b3Sopenharmony_ci
22753a5a1b3Sopenharmony_ci    char *server_name;
22853a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
22953a5a1b3Sopenharmony_ci    char *sink_name;
23053a5a1b3Sopenharmony_ci    char *configured_sink_name;
23153a5a1b3Sopenharmony_ci    pa_sink *sink;
23253a5a1b3Sopenharmony_ci    size_t requested_bytes;
23353a5a1b3Sopenharmony_ci#else
23453a5a1b3Sopenharmony_ci    char *source_name;
23553a5a1b3Sopenharmony_ci    char *configured_source_name;
23653a5a1b3Sopenharmony_ci    pa_source *source;
23753a5a1b3Sopenharmony_ci    pa_mcalign *mcalign;
23853a5a1b3Sopenharmony_ci#endif
23953a5a1b3Sopenharmony_ci
24053a5a1b3Sopenharmony_ci    pa_auth_cookie *auth_cookie;
24153a5a1b3Sopenharmony_ci
24253a5a1b3Sopenharmony_ci    uint32_t version;
24353a5a1b3Sopenharmony_ci    uint32_t ctag;
24453a5a1b3Sopenharmony_ci    uint32_t device_index;
24553a5a1b3Sopenharmony_ci    uint32_t channel;
24653a5a1b3Sopenharmony_ci    uint32_t latency;
24753a5a1b3Sopenharmony_ci
24853a5a1b3Sopenharmony_ci    int64_t counter;
24953a5a1b3Sopenharmony_ci    uint64_t receive_counter;
25053a5a1b3Sopenharmony_ci    uint64_t receive_snapshot;
25153a5a1b3Sopenharmony_ci
25253a5a1b3Sopenharmony_ci    bool remote_corked:1;
25353a5a1b3Sopenharmony_ci    bool remote_suspended:1;
25453a5a1b3Sopenharmony_ci    bool shutting_down:1;
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci    pa_usec_t transport_usec; /* maintained in the main thread */
25753a5a1b3Sopenharmony_ci    pa_usec_t thread_transport_usec; /* maintained in the IO thread */
25853a5a1b3Sopenharmony_ci
25953a5a1b3Sopenharmony_ci    uint32_t ignore_latency_before;
26053a5a1b3Sopenharmony_ci
26153a5a1b3Sopenharmony_ci    pa_time_event *time_event;
26253a5a1b3Sopenharmony_ci
26353a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
26453a5a1b3Sopenharmony_ci    pa_smoother_2 *smoother;
26553a5a1b3Sopenharmony_ci#else
26653a5a1b3Sopenharmony_ci    pa_smoother *smoother;
26753a5a1b3Sopenharmony_ci#endif
26853a5a1b3Sopenharmony_ci
26953a5a1b3Sopenharmony_ci    char *device_description;
27053a5a1b3Sopenharmony_ci    char *server_fqdn;
27153a5a1b3Sopenharmony_ci    char *user_name;
27253a5a1b3Sopenharmony_ci
27353a5a1b3Sopenharmony_ci    uint32_t maxlength;
27453a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
27553a5a1b3Sopenharmony_ci    uint32_t tlength;
27653a5a1b3Sopenharmony_ci    uint32_t minreq;
27753a5a1b3Sopenharmony_ci    uint32_t prebuf;
27853a5a1b3Sopenharmony_ci
27953a5a1b3Sopenharmony_ci    pa_proplist *sink_proplist;
28053a5a1b3Sopenharmony_ci#else
28153a5a1b3Sopenharmony_ci    uint32_t fragsize;
28253a5a1b3Sopenharmony_ci
28353a5a1b3Sopenharmony_ci    pa_proplist *source_proplist;
28453a5a1b3Sopenharmony_ci#endif
28553a5a1b3Sopenharmony_ci
28653a5a1b3Sopenharmony_ci    pa_sample_spec sample_spec;
28753a5a1b3Sopenharmony_ci    pa_channel_map channel_map;
28853a5a1b3Sopenharmony_ci
28953a5a1b3Sopenharmony_ci    tunnel_msg *msg;
29053a5a1b3Sopenharmony_ci
29153a5a1b3Sopenharmony_ci    pa_iochannel *io;
29253a5a1b3Sopenharmony_ci
29353a5a1b3Sopenharmony_ci    pa_usec_t reconnect_interval_us;
29453a5a1b3Sopenharmony_ci    pa_usec_t snapshot_time;
29553a5a1b3Sopenharmony_ci};
29653a5a1b3Sopenharmony_ci
29753a5a1b3Sopenharmony_cistruct module_restart_data {
29853a5a1b3Sopenharmony_ci    struct userdata *userdata;
29953a5a1b3Sopenharmony_ci    pa_restart_data *restart_data;
30053a5a1b3Sopenharmony_ci};
30153a5a1b3Sopenharmony_ci
30253a5a1b3Sopenharmony_cistatic void request_latency(struct userdata *u);
30353a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
30453a5a1b3Sopenharmony_cistatic void create_sink(struct userdata *u);
30553a5a1b3Sopenharmony_cistatic void on_sink_created(struct userdata *u);
30653a5a1b3Sopenharmony_ci#else
30753a5a1b3Sopenharmony_cistatic void create_source(struct userdata *u);
30853a5a1b3Sopenharmony_cistatic void on_source_created(struct userdata *u);
30953a5a1b3Sopenharmony_ci#endif
31053a5a1b3Sopenharmony_ci
31153a5a1b3Sopenharmony_ci/* Do a reinit of the module.  Note that u will be freed as a result of this
31253a5a1b3Sopenharmony_ci * call. */
31353a5a1b3Sopenharmony_cistatic void unload_module(struct module_restart_data *rd) {
31453a5a1b3Sopenharmony_ci    struct userdata *u = rd->userdata;
31553a5a1b3Sopenharmony_ci
31653a5a1b3Sopenharmony_ci    if (rd->restart_data) {
31753a5a1b3Sopenharmony_ci        pa_log_debug("Restart already pending");
31853a5a1b3Sopenharmony_ci        return;
31953a5a1b3Sopenharmony_ci    }
32053a5a1b3Sopenharmony_ci
32153a5a1b3Sopenharmony_ci    if (u->reconnect_interval_us > 0) {
32253a5a1b3Sopenharmony_ci        /* The handle returned here must be freed when do_init() was successful and when the
32353a5a1b3Sopenharmony_ci         * module exits. */
32453a5a1b3Sopenharmony_ci        rd->restart_data = pa_restart_module_reinit(u->module, do_init, do_done, u->reconnect_interval_us);
32553a5a1b3Sopenharmony_ci    } else
32653a5a1b3Sopenharmony_ci        pa_module_unload_request(u->module, true);
32753a5a1b3Sopenharmony_ci}
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci/* Called from main context */
33053a5a1b3Sopenharmony_cistatic void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
33153a5a1b3Sopenharmony_ci    pa_log_debug("Got stream or client event.");
33253a5a1b3Sopenharmony_ci}
33353a5a1b3Sopenharmony_ci
33453a5a1b3Sopenharmony_ci/* Called from main context */
33553a5a1b3Sopenharmony_cistatic void command_stream_killed(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
33653a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
33753a5a1b3Sopenharmony_ci
33853a5a1b3Sopenharmony_ci    pa_assert(pd);
33953a5a1b3Sopenharmony_ci    pa_assert(t);
34053a5a1b3Sopenharmony_ci    pa_assert(u);
34153a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
34253a5a1b3Sopenharmony_ci
34353a5a1b3Sopenharmony_ci    pa_log_warn("Stream killed");
34453a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
34553a5a1b3Sopenharmony_ci}
34653a5a1b3Sopenharmony_ci
34753a5a1b3Sopenharmony_ci/* Called from main context */
34853a5a1b3Sopenharmony_cistatic void command_overflow_or_underflow(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
34953a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
35053a5a1b3Sopenharmony_ci
35153a5a1b3Sopenharmony_ci    pa_assert(pd);
35253a5a1b3Sopenharmony_ci    pa_assert(t);
35353a5a1b3Sopenharmony_ci    pa_assert(u);
35453a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
35553a5a1b3Sopenharmony_ci
35653a5a1b3Sopenharmony_ci    pa_log_info("Server signalled buffer overrun/underrun.");
35753a5a1b3Sopenharmony_ci    request_latency(u);
35853a5a1b3Sopenharmony_ci}
35953a5a1b3Sopenharmony_ci
36053a5a1b3Sopenharmony_ci/* Called from main context */
36153a5a1b3Sopenharmony_cistatic void command_suspended(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
36253a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
36353a5a1b3Sopenharmony_ci    uint32_t channel;
36453a5a1b3Sopenharmony_ci    bool suspended;
36553a5a1b3Sopenharmony_ci
36653a5a1b3Sopenharmony_ci    pa_assert(pd);
36753a5a1b3Sopenharmony_ci    pa_assert(t);
36853a5a1b3Sopenharmony_ci    pa_assert(u);
36953a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
37053a5a1b3Sopenharmony_ci
37153a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &channel) < 0 ||
37253a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &suspended) < 0 ||
37353a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci        pa_log("Invalid packet.");
37653a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
37753a5a1b3Sopenharmony_ci        return;
37853a5a1b3Sopenharmony_ci    }
37953a5a1b3Sopenharmony_ci
38053a5a1b3Sopenharmony_ci    pa_log_debug("Server reports device suspend.");
38153a5a1b3Sopenharmony_ci
38253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
38353a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
38453a5a1b3Sopenharmony_ci#else
38553a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
38653a5a1b3Sopenharmony_ci#endif
38753a5a1b3Sopenharmony_ci
38853a5a1b3Sopenharmony_ci    request_latency(u);
38953a5a1b3Sopenharmony_ci}
39053a5a1b3Sopenharmony_ci
39153a5a1b3Sopenharmony_ci/* Called from main context */
39253a5a1b3Sopenharmony_cistatic void command_moved(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
39353a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
39453a5a1b3Sopenharmony_ci    uint32_t channel, di;
39553a5a1b3Sopenharmony_ci    const char *dn;
39653a5a1b3Sopenharmony_ci    bool suspended;
39753a5a1b3Sopenharmony_ci
39853a5a1b3Sopenharmony_ci    pa_assert(pd);
39953a5a1b3Sopenharmony_ci    pa_assert(t);
40053a5a1b3Sopenharmony_ci    pa_assert(u);
40153a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &channel) < 0 ||
40453a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &di) < 0 ||
40553a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &dn) < 0 ||
40653a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &suspended) < 0) {
40753a5a1b3Sopenharmony_ci
40853a5a1b3Sopenharmony_ci        pa_log_error("Invalid packet.");
40953a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
41053a5a1b3Sopenharmony_ci        return;
41153a5a1b3Sopenharmony_ci    }
41253a5a1b3Sopenharmony_ci
41353a5a1b3Sopenharmony_ci    pa_log_debug("Server reports a stream move.");
41453a5a1b3Sopenharmony_ci
41553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
41653a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
41753a5a1b3Sopenharmony_ci#else
41853a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
41953a5a1b3Sopenharmony_ci#endif
42053a5a1b3Sopenharmony_ci
42153a5a1b3Sopenharmony_ci    request_latency(u);
42253a5a1b3Sopenharmony_ci}
42353a5a1b3Sopenharmony_ci
42453a5a1b3Sopenharmony_cistatic void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
42553a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
42653a5a1b3Sopenharmony_ci    uint32_t channel, maxlength, tlength = 0, fragsize, prebuf, minreq;
42753a5a1b3Sopenharmony_ci    pa_usec_t usec;
42853a5a1b3Sopenharmony_ci
42953a5a1b3Sopenharmony_ci    pa_assert(pd);
43053a5a1b3Sopenharmony_ci    pa_assert(t);
43153a5a1b3Sopenharmony_ci    pa_assert(u);
43253a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
43353a5a1b3Sopenharmony_ci
43453a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &channel) < 0 ||
43553a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &maxlength) < 0) {
43653a5a1b3Sopenharmony_ci
43753a5a1b3Sopenharmony_ci        pa_log_error("Invalid packet.");
43853a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
43953a5a1b3Sopenharmony_ci        return;
44053a5a1b3Sopenharmony_ci    }
44153a5a1b3Sopenharmony_ci
44253a5a1b3Sopenharmony_ci    if (command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED) {
44353a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &fragsize) < 0 ||
44453a5a1b3Sopenharmony_ci            pa_tagstruct_get_usec(t, &usec) < 0) {
44553a5a1b3Sopenharmony_ci
44653a5a1b3Sopenharmony_ci            pa_log_error("Invalid packet.");
44753a5a1b3Sopenharmony_ci            unload_module(u->module->userdata);
44853a5a1b3Sopenharmony_ci            return;
44953a5a1b3Sopenharmony_ci        }
45053a5a1b3Sopenharmony_ci    } else {
45153a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &tlength) < 0 ||
45253a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &prebuf) < 0 ||
45353a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &minreq) < 0 ||
45453a5a1b3Sopenharmony_ci            pa_tagstruct_get_usec(t, &usec) < 0) {
45553a5a1b3Sopenharmony_ci
45653a5a1b3Sopenharmony_ci            pa_log_error("Invalid packet.");
45753a5a1b3Sopenharmony_ci            unload_module(u->module->userdata);
45853a5a1b3Sopenharmony_ci            return;
45953a5a1b3Sopenharmony_ci        }
46053a5a1b3Sopenharmony_ci    }
46153a5a1b3Sopenharmony_ci
46253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
46353a5a1b3Sopenharmony_ci    pa_log_debug("Server reports buffer attrs changed. tlength now at %lu, before %lu.", (unsigned long) tlength, (unsigned long) u->tlength);
46453a5a1b3Sopenharmony_ci#endif
46553a5a1b3Sopenharmony_ci
46653a5a1b3Sopenharmony_ci    request_latency(u);
46753a5a1b3Sopenharmony_ci}
46853a5a1b3Sopenharmony_ci
46953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
47053a5a1b3Sopenharmony_ci
47153a5a1b3Sopenharmony_ci/* Called from main context */
47253a5a1b3Sopenharmony_cistatic void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
47353a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
47453a5a1b3Sopenharmony_ci
47553a5a1b3Sopenharmony_ci    pa_assert(pd);
47653a5a1b3Sopenharmony_ci    pa_assert(t);
47753a5a1b3Sopenharmony_ci    pa_assert(u);
47853a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
47953a5a1b3Sopenharmony_ci
48053a5a1b3Sopenharmony_ci    pa_log_debug("Server reports playback started.");
48153a5a1b3Sopenharmony_ci    request_latency(u);
48253a5a1b3Sopenharmony_ci}
48353a5a1b3Sopenharmony_ci
48453a5a1b3Sopenharmony_ci#endif
48553a5a1b3Sopenharmony_ci
48653a5a1b3Sopenharmony_ci/* Called from IO thread context */
48753a5a1b3Sopenharmony_cistatic void check_smoother_status(struct userdata *u, bool past) {
48853a5a1b3Sopenharmony_ci    pa_usec_t x;
48953a5a1b3Sopenharmony_ci
49053a5a1b3Sopenharmony_ci    pa_assert(u);
49153a5a1b3Sopenharmony_ci
49253a5a1b3Sopenharmony_ci    x = pa_rtclock_now();
49353a5a1b3Sopenharmony_ci
49453a5a1b3Sopenharmony_ci    /* Correct by the time the requested issued needs to travel to the
49553a5a1b3Sopenharmony_ci     * other side.  This is a valid thread-safe access, because the
49653a5a1b3Sopenharmony_ci     * main thread is waiting for us */
49753a5a1b3Sopenharmony_ci
49853a5a1b3Sopenharmony_ci    if (past)
49953a5a1b3Sopenharmony_ci        x -= u->thread_transport_usec;
50053a5a1b3Sopenharmony_ci    else
50153a5a1b3Sopenharmony_ci        x += u->thread_transport_usec;
50253a5a1b3Sopenharmony_ci
50353a5a1b3Sopenharmony_ci    if (u->remote_suspended || u->remote_corked)
50453a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
50553a5a1b3Sopenharmony_ci        pa_smoother_2_pause(u->smoother, x);
50653a5a1b3Sopenharmony_ci    else
50753a5a1b3Sopenharmony_ci        pa_smoother_2_resume(u->smoother, x);
50853a5a1b3Sopenharmony_ci#else
50953a5a1b3Sopenharmony_ci        pa_smoother_pause(u->smoother, x);
51053a5a1b3Sopenharmony_ci    else
51153a5a1b3Sopenharmony_ci        pa_smoother_resume(u->smoother, x, true);
51253a5a1b3Sopenharmony_ci#endif
51353a5a1b3Sopenharmony_ci}
51453a5a1b3Sopenharmony_ci
51553a5a1b3Sopenharmony_ci/* Called from IO thread context */
51653a5a1b3Sopenharmony_cistatic void stream_cork_within_thread(struct userdata *u, bool cork) {
51753a5a1b3Sopenharmony_ci    pa_assert(u);
51853a5a1b3Sopenharmony_ci
51953a5a1b3Sopenharmony_ci    if (u->remote_corked == cork)
52053a5a1b3Sopenharmony_ci        return;
52153a5a1b3Sopenharmony_ci
52253a5a1b3Sopenharmony_ci    u->remote_corked = cork;
52353a5a1b3Sopenharmony_ci    check_smoother_status(u, false);
52453a5a1b3Sopenharmony_ci}
52553a5a1b3Sopenharmony_ci
52653a5a1b3Sopenharmony_ci/* Called from main context */
52753a5a1b3Sopenharmony_cistatic void stream_cork(struct userdata *u, bool cork) {
52853a5a1b3Sopenharmony_ci    pa_tagstruct *t;
52953a5a1b3Sopenharmony_ci    pa_assert(u);
53053a5a1b3Sopenharmony_ci
53153a5a1b3Sopenharmony_ci    if (!u->pstream)
53253a5a1b3Sopenharmony_ci        return;
53353a5a1b3Sopenharmony_ci
53453a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
53553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
53653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
53753a5a1b3Sopenharmony_ci#else
53853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_CORK_RECORD_STREAM);
53953a5a1b3Sopenharmony_ci#endif
54053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->ctag++);
54153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->channel);
54253a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, cork);
54353a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
54453a5a1b3Sopenharmony_ci
54553a5a1b3Sopenharmony_ci    request_latency(u);
54653a5a1b3Sopenharmony_ci}
54753a5a1b3Sopenharmony_ci
54853a5a1b3Sopenharmony_ci/* Called from IO thread context */
54953a5a1b3Sopenharmony_cistatic void stream_suspend_within_thread(struct userdata *u, bool suspend) {
55053a5a1b3Sopenharmony_ci    pa_assert(u);
55153a5a1b3Sopenharmony_ci
55253a5a1b3Sopenharmony_ci    if (u->remote_suspended == suspend)
55353a5a1b3Sopenharmony_ci        return;
55453a5a1b3Sopenharmony_ci
55553a5a1b3Sopenharmony_ci    u->remote_suspended = suspend;
55653a5a1b3Sopenharmony_ci    check_smoother_status(u, true);
55753a5a1b3Sopenharmony_ci}
55853a5a1b3Sopenharmony_ci
55953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
56053a5a1b3Sopenharmony_ci
56153a5a1b3Sopenharmony_ci/* Called from IO thread context */
56253a5a1b3Sopenharmony_cistatic void send_data(struct userdata *u) {
56353a5a1b3Sopenharmony_ci    pa_assert(u);
56453a5a1b3Sopenharmony_ci
56553a5a1b3Sopenharmony_ci    while (u->requested_bytes > 0) {
56653a5a1b3Sopenharmony_ci        pa_memchunk memchunk;
56753a5a1b3Sopenharmony_ci
56853a5a1b3Sopenharmony_ci        pa_sink_render(u->sink, u->requested_bytes, &memchunk);
56953a5a1b3Sopenharmony_ci        pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL);
57053a5a1b3Sopenharmony_ci        pa_memblock_unref(memchunk.memblock);
57153a5a1b3Sopenharmony_ci
57253a5a1b3Sopenharmony_ci        u->requested_bytes -= memchunk.length;
57353a5a1b3Sopenharmony_ci
57453a5a1b3Sopenharmony_ci        u->counter += (int64_t) memchunk.length;
57553a5a1b3Sopenharmony_ci    }
57653a5a1b3Sopenharmony_ci}
57753a5a1b3Sopenharmony_ci
57853a5a1b3Sopenharmony_ci/* This function is called from IO context -- except when it is not. */
57953a5a1b3Sopenharmony_cistatic int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
58053a5a1b3Sopenharmony_ci    struct userdata *u = PA_SINK(o)->userdata;
58153a5a1b3Sopenharmony_ci
58253a5a1b3Sopenharmony_ci    switch (code) {
58353a5a1b3Sopenharmony_ci
58453a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_SET_STATE: {
58553a5a1b3Sopenharmony_ci            int r;
58653a5a1b3Sopenharmony_ci
58753a5a1b3Sopenharmony_ci            /* First, change the state, because otherwise pa_sink_render() would fail */
58853a5a1b3Sopenharmony_ci            if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
58953a5a1b3Sopenharmony_ci
59053a5a1b3Sopenharmony_ci                stream_cork_within_thread(u, u->sink->thread_info.state == PA_SINK_SUSPENDED);
59153a5a1b3Sopenharmony_ci
59253a5a1b3Sopenharmony_ci                if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
59353a5a1b3Sopenharmony_ci                    send_data(u);
59453a5a1b3Sopenharmony_ci            }
59553a5a1b3Sopenharmony_ci
59653a5a1b3Sopenharmony_ci            return r;
59753a5a1b3Sopenharmony_ci        }
59853a5a1b3Sopenharmony_ci
59953a5a1b3Sopenharmony_ci        case PA_SINK_MESSAGE_GET_LATENCY: {
60053a5a1b3Sopenharmony_ci            int64_t *usec = data;
60153a5a1b3Sopenharmony_ci
60253a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
60353a5a1b3Sopenharmony_ci            *usec = pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), u->counter);
60453a5a1b3Sopenharmony_ci#else
60553a5a1b3Sopenharmony_ci            pa_usec_t yl, yr;
60653a5a1b3Sopenharmony_ci
60753a5a1b3Sopenharmony_ci            yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
60853a5a1b3Sopenharmony_ci            yr = pa_smoother_get(u->smoother, pa_rtclock_now());
60953a5a1b3Sopenharmony_ci
61053a5a1b3Sopenharmony_ci            *usec = (int64_t)yl - yr;
61153a5a1b3Sopenharmony_ci#endif
61253a5a1b3Sopenharmony_ci            return 0;
61353a5a1b3Sopenharmony_ci        }
61453a5a1b3Sopenharmony_ci
61553a5a1b3Sopenharmony_ci        case SINK_MESSAGE_GET_LATENCY_SNAPSHOT: {
61653a5a1b3Sopenharmony_ci            int64_t *send_counter = data;
61753a5a1b3Sopenharmony_ci
61853a5a1b3Sopenharmony_ci            *send_counter = u->counter;
61953a5a1b3Sopenharmony_ci            return 0;
62053a5a1b3Sopenharmony_ci        }
62153a5a1b3Sopenharmony_ci
62253a5a1b3Sopenharmony_ci        case SINK_MESSAGE_REQUEST:
62353a5a1b3Sopenharmony_ci
62453a5a1b3Sopenharmony_ci            pa_assert(offset > 0);
62553a5a1b3Sopenharmony_ci            u->requested_bytes += (size_t) offset;
62653a5a1b3Sopenharmony_ci
62753a5a1b3Sopenharmony_ci            if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
62853a5a1b3Sopenharmony_ci                send_data(u);
62953a5a1b3Sopenharmony_ci
63053a5a1b3Sopenharmony_ci            return 0;
63153a5a1b3Sopenharmony_ci
63253a5a1b3Sopenharmony_ci        case SINK_MESSAGE_REMOTE_SUSPEND:
63353a5a1b3Sopenharmony_ci
63453a5a1b3Sopenharmony_ci            stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
63553a5a1b3Sopenharmony_ci            return 0;
63653a5a1b3Sopenharmony_ci
63753a5a1b3Sopenharmony_ci        case SINK_MESSAGE_UPDATE_LATENCY: {
63853a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
63953a5a1b3Sopenharmony_ci            int64_t bytes;
64053a5a1b3Sopenharmony_ci
64153a5a1b3Sopenharmony_ci            if (offset < 0)
64253a5a1b3Sopenharmony_ci                bytes = - pa_usec_to_bytes(- offset, &u->sink->sample_spec);
64353a5a1b3Sopenharmony_ci            else
64453a5a1b3Sopenharmony_ci                bytes = pa_usec_to_bytes(offset, &u->sink->sample_spec);
64553a5a1b3Sopenharmony_ci
64653a5a1b3Sopenharmony_ci            if (u->counter > bytes)
64753a5a1b3Sopenharmony_ci                bytes = u->counter - bytes;
64853a5a1b3Sopenharmony_ci            else
64953a5a1b3Sopenharmony_ci                bytes = 0;
65053a5a1b3Sopenharmony_ci
65153a5a1b3Sopenharmony_ci            /* We may use u->snapshot time because the main thread is waiting */
65253a5a1b3Sopenharmony_ci             pa_smoother_2_put(u->smoother, u->snapshot_time, bytes);
65353a5a1b3Sopenharmony_ci#else
65453a5a1b3Sopenharmony_ci            pa_usec_t y;
65553a5a1b3Sopenharmony_ci
65653a5a1b3Sopenharmony_ci            y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
65753a5a1b3Sopenharmony_ci
65853a5a1b3Sopenharmony_ci            if (y > (pa_usec_t) offset)
65953a5a1b3Sopenharmony_ci                y -= (pa_usec_t) offset;
66053a5a1b3Sopenharmony_ci            else
66153a5a1b3Sopenharmony_ci                y = 0;
66253a5a1b3Sopenharmony_ci
66353a5a1b3Sopenharmony_ci            /* We may use u->snapshot time because the main thread is waiting */
66453a5a1b3Sopenharmony_ci            pa_smoother_put(u->smoother, u->snapshot_time, y);
66553a5a1b3Sopenharmony_ci#endif
66653a5a1b3Sopenharmony_ci
66753a5a1b3Sopenharmony_ci            /* We can access this freely here, since the main thread is waiting for us */
66853a5a1b3Sopenharmony_ci            u->thread_transport_usec = u->transport_usec;
66953a5a1b3Sopenharmony_ci
67053a5a1b3Sopenharmony_ci            return 0;
67153a5a1b3Sopenharmony_ci        }
67253a5a1b3Sopenharmony_ci
67353a5a1b3Sopenharmony_ci        case SINK_MESSAGE_POST:
67453a5a1b3Sopenharmony_ci
67553a5a1b3Sopenharmony_ci            /* OK, This might be a bit confusing. This message is
67653a5a1b3Sopenharmony_ci             * delivered to us from the main context -- NOT from the
67753a5a1b3Sopenharmony_ci             * IO thread context where the rest of the messages are
67853a5a1b3Sopenharmony_ci             * dispatched. Yeah, ugly, but I am a lazy bastard. */
67953a5a1b3Sopenharmony_ci
68053a5a1b3Sopenharmony_ci            pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk);
68153a5a1b3Sopenharmony_ci
68253a5a1b3Sopenharmony_ci            u->receive_counter += chunk->length;
68353a5a1b3Sopenharmony_ci
68453a5a1b3Sopenharmony_ci            return 0;
68553a5a1b3Sopenharmony_ci    }
68653a5a1b3Sopenharmony_ci
68753a5a1b3Sopenharmony_ci    return pa_sink_process_msg(o, code, data, offset, chunk);
68853a5a1b3Sopenharmony_ci}
68953a5a1b3Sopenharmony_ci
69053a5a1b3Sopenharmony_ci/* Called from main context */
69153a5a1b3Sopenharmony_cistatic int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) {
69253a5a1b3Sopenharmony_ci    struct userdata *u;
69353a5a1b3Sopenharmony_ci    pa_sink_assert_ref(s);
69453a5a1b3Sopenharmony_ci    u = s->userdata;
69553a5a1b3Sopenharmony_ci
69653a5a1b3Sopenharmony_ci    /* It may be that only the suspend cause is changing, in which
69753a5a1b3Sopenharmony_ci     * case there's nothing to do. */
69853a5a1b3Sopenharmony_ci    if (state == s->state)
69953a5a1b3Sopenharmony_ci        return 0;
70053a5a1b3Sopenharmony_ci
70153a5a1b3Sopenharmony_ci    switch ((pa_sink_state_t) state) {
70253a5a1b3Sopenharmony_ci
70353a5a1b3Sopenharmony_ci        case PA_SINK_SUSPENDED:
70453a5a1b3Sopenharmony_ci            pa_assert(PA_SINK_IS_OPENED(s->state));
70553a5a1b3Sopenharmony_ci            stream_cork(u, true);
70653a5a1b3Sopenharmony_ci            break;
70753a5a1b3Sopenharmony_ci
70853a5a1b3Sopenharmony_ci        case PA_SINK_IDLE:
70953a5a1b3Sopenharmony_ci        case PA_SINK_RUNNING:
71053a5a1b3Sopenharmony_ci            if (s->state == PA_SINK_SUSPENDED)
71153a5a1b3Sopenharmony_ci                stream_cork(u, false);
71253a5a1b3Sopenharmony_ci            break;
71353a5a1b3Sopenharmony_ci
71453a5a1b3Sopenharmony_ci        case PA_SINK_UNLINKED:
71553a5a1b3Sopenharmony_ci        case PA_SINK_INIT:
71653a5a1b3Sopenharmony_ci        case PA_SINK_INVALID_STATE:
71753a5a1b3Sopenharmony_ci            ;
71853a5a1b3Sopenharmony_ci    }
71953a5a1b3Sopenharmony_ci
72053a5a1b3Sopenharmony_ci    return 0;
72153a5a1b3Sopenharmony_ci}
72253a5a1b3Sopenharmony_ci
72353a5a1b3Sopenharmony_ci#else
72453a5a1b3Sopenharmony_ci
72553a5a1b3Sopenharmony_ci/* This function is called from IO context -- except when it is not. */
72653a5a1b3Sopenharmony_cistatic int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
72753a5a1b3Sopenharmony_ci    struct userdata *u = PA_SOURCE(o)->userdata;
72853a5a1b3Sopenharmony_ci
72953a5a1b3Sopenharmony_ci    switch (code) {
73053a5a1b3Sopenharmony_ci
73153a5a1b3Sopenharmony_ci        case PA_SOURCE_MESSAGE_SET_STATE: {
73253a5a1b3Sopenharmony_ci            int r;
73353a5a1b3Sopenharmony_ci
73453a5a1b3Sopenharmony_ci            if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0)
73553a5a1b3Sopenharmony_ci                stream_cork_within_thread(u, u->source->thread_info.state == PA_SOURCE_SUSPENDED);
73653a5a1b3Sopenharmony_ci
73753a5a1b3Sopenharmony_ci            return r;
73853a5a1b3Sopenharmony_ci        }
73953a5a1b3Sopenharmony_ci
74053a5a1b3Sopenharmony_ci        case PA_SOURCE_MESSAGE_GET_LATENCY: {
74153a5a1b3Sopenharmony_ci            int64_t *usec = data;
74253a5a1b3Sopenharmony_ci
74353a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
74453a5a1b3Sopenharmony_ci            *usec = - pa_smoother_2_get_delay(u->smoother, pa_rtclock_now(), u->counter);
74553a5a1b3Sopenharmony_ci#else
74653a5a1b3Sopenharmony_ci            pa_usec_t yr, yl;
74753a5a1b3Sopenharmony_ci
74853a5a1b3Sopenharmony_ci            yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
74953a5a1b3Sopenharmony_ci            yr = pa_smoother_get(u->smoother, pa_rtclock_now());
75053a5a1b3Sopenharmony_ci
75153a5a1b3Sopenharmony_ci            *usec = (int64_t)yr - yl;
75253a5a1b3Sopenharmony_ci#endif
75353a5a1b3Sopenharmony_ci            return 0;
75453a5a1b3Sopenharmony_ci        }
75553a5a1b3Sopenharmony_ci
75653a5a1b3Sopenharmony_ci        case SOURCE_MESSAGE_GET_LATENCY_SNAPSHOT: {
75753a5a1b3Sopenharmony_ci            int64_t *send_counter = data;
75853a5a1b3Sopenharmony_ci
75953a5a1b3Sopenharmony_ci            *send_counter = u->counter;
76053a5a1b3Sopenharmony_ci            return 0;
76153a5a1b3Sopenharmony_ci        }
76253a5a1b3Sopenharmony_ci
76353a5a1b3Sopenharmony_ci        case SOURCE_MESSAGE_POST: {
76453a5a1b3Sopenharmony_ci            pa_memchunk c;
76553a5a1b3Sopenharmony_ci
76653a5a1b3Sopenharmony_ci            pa_mcalign_push(u->mcalign, chunk);
76753a5a1b3Sopenharmony_ci
76853a5a1b3Sopenharmony_ci            while (pa_mcalign_pop(u->mcalign, &c) >= 0) {
76953a5a1b3Sopenharmony_ci
77053a5a1b3Sopenharmony_ci                if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
77153a5a1b3Sopenharmony_ci                    pa_source_post(u->source, &c);
77253a5a1b3Sopenharmony_ci
77353a5a1b3Sopenharmony_ci                pa_memblock_unref(c.memblock);
77453a5a1b3Sopenharmony_ci
77553a5a1b3Sopenharmony_ci                u->counter += (int64_t) c.length;
77653a5a1b3Sopenharmony_ci            }
77753a5a1b3Sopenharmony_ci
77853a5a1b3Sopenharmony_ci            return 0;
77953a5a1b3Sopenharmony_ci        }
78053a5a1b3Sopenharmony_ci
78153a5a1b3Sopenharmony_ci        case SOURCE_MESSAGE_REMOTE_SUSPEND:
78253a5a1b3Sopenharmony_ci
78353a5a1b3Sopenharmony_ci            stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
78453a5a1b3Sopenharmony_ci            return 0;
78553a5a1b3Sopenharmony_ci
78653a5a1b3Sopenharmony_ci        case SOURCE_MESSAGE_UPDATE_LATENCY: {
78753a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
78853a5a1b3Sopenharmony_ci            int64_t bytes;
78953a5a1b3Sopenharmony_ci
79053a5a1b3Sopenharmony_ci            if (offset < 0)
79153a5a1b3Sopenharmony_ci                bytes = - pa_usec_to_bytes(- offset, &u->source->sample_spec);
79253a5a1b3Sopenharmony_ci            else
79353a5a1b3Sopenharmony_ci                bytes = pa_usec_to_bytes(offset, &u->source->sample_spec);
79453a5a1b3Sopenharmony_ci
79553a5a1b3Sopenharmony_ci            bytes += u->counter;
79653a5a1b3Sopenharmony_ci
79753a5a1b3Sopenharmony_ci            /* We may use u->snapshot time because the main thread is waiting */
79853a5a1b3Sopenharmony_ci            pa_smoother_2_put(u->smoother, u->snapshot_time, bytes);
79953a5a1b3Sopenharmony_ci#else
80053a5a1b3Sopenharmony_ci            pa_usec_t y;
80153a5a1b3Sopenharmony_ci
80253a5a1b3Sopenharmony_ci            y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
80353a5a1b3Sopenharmony_ci            y += offset;
80453a5a1b3Sopenharmony_ci
80553a5a1b3Sopenharmony_ci            /* We may use u->snapshot time because the main thread is waiting */
80653a5a1b3Sopenharmony_ci            pa_smoother_put(u->smoother, u->snapshot_time, y);
80753a5a1b3Sopenharmony_ci#endif
80853a5a1b3Sopenharmony_ci
80953a5a1b3Sopenharmony_ci            /* We can access this freely here, since the main thread is waiting for us */
81053a5a1b3Sopenharmony_ci            u->thread_transport_usec = u->transport_usec;
81153a5a1b3Sopenharmony_ci
81253a5a1b3Sopenharmony_ci            return 0;
81353a5a1b3Sopenharmony_ci        }
81453a5a1b3Sopenharmony_ci    }
81553a5a1b3Sopenharmony_ci
81653a5a1b3Sopenharmony_ci    return pa_source_process_msg(o, code, data, offset, chunk);
81753a5a1b3Sopenharmony_ci}
81853a5a1b3Sopenharmony_ci
81953a5a1b3Sopenharmony_ci/* Called from main context */
82053a5a1b3Sopenharmony_cistatic int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause) {
82153a5a1b3Sopenharmony_ci    struct userdata *u;
82253a5a1b3Sopenharmony_ci    pa_source_assert_ref(s);
82353a5a1b3Sopenharmony_ci    u = s->userdata;
82453a5a1b3Sopenharmony_ci
82553a5a1b3Sopenharmony_ci    /* It may be that only the suspend cause is changing, in which
82653a5a1b3Sopenharmony_ci     * case there's nothing to do. */
82753a5a1b3Sopenharmony_ci    if (state == s->state)
82853a5a1b3Sopenharmony_ci        return 0;
82953a5a1b3Sopenharmony_ci
83053a5a1b3Sopenharmony_ci    switch ((pa_source_state_t) state) {
83153a5a1b3Sopenharmony_ci
83253a5a1b3Sopenharmony_ci        case PA_SOURCE_SUSPENDED:
83353a5a1b3Sopenharmony_ci            pa_assert(PA_SOURCE_IS_OPENED(s->state));
83453a5a1b3Sopenharmony_ci            stream_cork(u, true);
83553a5a1b3Sopenharmony_ci            break;
83653a5a1b3Sopenharmony_ci
83753a5a1b3Sopenharmony_ci        case PA_SOURCE_IDLE:
83853a5a1b3Sopenharmony_ci        case PA_SOURCE_RUNNING:
83953a5a1b3Sopenharmony_ci            if (s->state == PA_SOURCE_SUSPENDED)
84053a5a1b3Sopenharmony_ci                stream_cork(u, false);
84153a5a1b3Sopenharmony_ci            break;
84253a5a1b3Sopenharmony_ci
84353a5a1b3Sopenharmony_ci        case PA_SOURCE_UNLINKED:
84453a5a1b3Sopenharmony_ci        case PA_SOURCE_INIT:
84553a5a1b3Sopenharmony_ci        case PA_SOURCE_INVALID_STATE:
84653a5a1b3Sopenharmony_ci            ;
84753a5a1b3Sopenharmony_ci    }
84853a5a1b3Sopenharmony_ci
84953a5a1b3Sopenharmony_ci    return 0;
85053a5a1b3Sopenharmony_ci}
85153a5a1b3Sopenharmony_ci
85253a5a1b3Sopenharmony_ci#endif
85353a5a1b3Sopenharmony_ci
85453a5a1b3Sopenharmony_cistatic void thread_func(void *userdata) {
85553a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
85653a5a1b3Sopenharmony_ci
85753a5a1b3Sopenharmony_ci    pa_assert(u);
85853a5a1b3Sopenharmony_ci
85953a5a1b3Sopenharmony_ci    pa_log_debug("Thread starting up");
86053a5a1b3Sopenharmony_ci
86153a5a1b3Sopenharmony_ci    pa_thread_mq_install(&u->thread_mq);
86253a5a1b3Sopenharmony_ci
86353a5a1b3Sopenharmony_ci    for (;;) {
86453a5a1b3Sopenharmony_ci        int ret;
86553a5a1b3Sopenharmony_ci
86653a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
86753a5a1b3Sopenharmony_ci        if (u->sink && PA_UNLIKELY(u->sink->thread_info.rewind_requested))
86853a5a1b3Sopenharmony_ci            pa_sink_process_rewind(u->sink, 0);
86953a5a1b3Sopenharmony_ci#endif
87053a5a1b3Sopenharmony_ci
87153a5a1b3Sopenharmony_ci        if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
87253a5a1b3Sopenharmony_ci            goto fail;
87353a5a1b3Sopenharmony_ci
87453a5a1b3Sopenharmony_ci        if (ret == 0)
87553a5a1b3Sopenharmony_ci            goto finish;
87653a5a1b3Sopenharmony_ci    }
87753a5a1b3Sopenharmony_ci
87853a5a1b3Sopenharmony_cifail:
87953a5a1b3Sopenharmony_ci    /* If this was no regular exit from the loop we have to continue
88053a5a1b3Sopenharmony_ci     * processing messages until we received PA_MESSAGE_SHUTDOWN */
88153a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->msg), TUNNEL_MESSAGE_MAYBE_RESTART, u, 0, NULL, NULL);
88253a5a1b3Sopenharmony_ci    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
88353a5a1b3Sopenharmony_ci
88453a5a1b3Sopenharmony_cifinish:
88553a5a1b3Sopenharmony_ci    pa_log_debug("Thread shutting down");
88653a5a1b3Sopenharmony_ci}
88753a5a1b3Sopenharmony_ci
88853a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
88953a5a1b3Sopenharmony_ci/* Called from main context */
89053a5a1b3Sopenharmony_cistatic void command_request(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
89153a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
89253a5a1b3Sopenharmony_ci    uint32_t bytes, channel;
89353a5a1b3Sopenharmony_ci
89453a5a1b3Sopenharmony_ci    pa_assert(pd);
89553a5a1b3Sopenharmony_ci    pa_assert(command == PA_COMMAND_REQUEST);
89653a5a1b3Sopenharmony_ci    pa_assert(t);
89753a5a1b3Sopenharmony_ci    pa_assert(u);
89853a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
89953a5a1b3Sopenharmony_ci
90053a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &channel) < 0 ||
90153a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &bytes) < 0) {
90253a5a1b3Sopenharmony_ci        pa_log("Invalid protocol reply");
90353a5a1b3Sopenharmony_ci        goto fail;
90453a5a1b3Sopenharmony_ci    }
90553a5a1b3Sopenharmony_ci
90653a5a1b3Sopenharmony_ci    if (channel != u->channel) {
90753a5a1b3Sopenharmony_ci        pa_log("Received data for invalid channel");
90853a5a1b3Sopenharmony_ci        goto fail;
90953a5a1b3Sopenharmony_ci    }
91053a5a1b3Sopenharmony_ci
91153a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
91253a5a1b3Sopenharmony_ci    return;
91353a5a1b3Sopenharmony_ci
91453a5a1b3Sopenharmony_cifail:
91553a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
91653a5a1b3Sopenharmony_ci}
91753a5a1b3Sopenharmony_ci
91853a5a1b3Sopenharmony_ci#endif
91953a5a1b3Sopenharmony_ci
92053a5a1b3Sopenharmony_ci/* Called from main context */
92153a5a1b3Sopenharmony_cistatic void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
92253a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
92353a5a1b3Sopenharmony_ci    pa_usec_t sink_usec, source_usec;
92453a5a1b3Sopenharmony_ci    bool playing;
92553a5a1b3Sopenharmony_ci    int64_t write_index, read_index;
92653a5a1b3Sopenharmony_ci    struct timeval local, remote, now;
92753a5a1b3Sopenharmony_ci    pa_sample_spec *ss;
92853a5a1b3Sopenharmony_ci    int64_t delay;
92953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
93053a5a1b3Sopenharmony_ci    uint64_t send_counter;
93153a5a1b3Sopenharmony_ci#endif
93253a5a1b3Sopenharmony_ci
93353a5a1b3Sopenharmony_ci    pa_assert(pd);
93453a5a1b3Sopenharmony_ci    pa_assert(u);
93553a5a1b3Sopenharmony_ci
93653a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY) {
93753a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
93853a5a1b3Sopenharmony_ci            pa_log("Failed to get latency.");
93953a5a1b3Sopenharmony_ci        else
94053a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
94153a5a1b3Sopenharmony_ci        goto fail;
94253a5a1b3Sopenharmony_ci    }
94353a5a1b3Sopenharmony_ci
94453a5a1b3Sopenharmony_ci    if (pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
94553a5a1b3Sopenharmony_ci        pa_tagstruct_get_usec(t, &source_usec) < 0 ||
94653a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &playing) < 0 ||
94753a5a1b3Sopenharmony_ci        pa_tagstruct_get_timeval(t, &local) < 0 ||
94853a5a1b3Sopenharmony_ci        pa_tagstruct_get_timeval(t, &remote) < 0 ||
94953a5a1b3Sopenharmony_ci        pa_tagstruct_gets64(t, &write_index) < 0 ||
95053a5a1b3Sopenharmony_ci        pa_tagstruct_gets64(t, &read_index) < 0) {
95153a5a1b3Sopenharmony_ci        pa_log("Invalid reply.");
95253a5a1b3Sopenharmony_ci        goto fail;
95353a5a1b3Sopenharmony_ci    }
95453a5a1b3Sopenharmony_ci
95553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
95653a5a1b3Sopenharmony_ci    if (u->version >= 13) {
95753a5a1b3Sopenharmony_ci        uint64_t underrun_for = 0, playing_for = 0;
95853a5a1b3Sopenharmony_ci
95953a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
96053a5a1b3Sopenharmony_ci            pa_tagstruct_getu64(t, &playing_for) < 0) {
96153a5a1b3Sopenharmony_ci            pa_log("Invalid reply.");
96253a5a1b3Sopenharmony_ci            goto fail;
96353a5a1b3Sopenharmony_ci        }
96453a5a1b3Sopenharmony_ci    }
96553a5a1b3Sopenharmony_ci#endif
96653a5a1b3Sopenharmony_ci
96753a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
96853a5a1b3Sopenharmony_ci        pa_log("Invalid reply.");
96953a5a1b3Sopenharmony_ci        goto fail;
97053a5a1b3Sopenharmony_ci    }
97153a5a1b3Sopenharmony_ci
97253a5a1b3Sopenharmony_ci    if (tag < u->ignore_latency_before) {
97353a5a1b3Sopenharmony_ci        return;
97453a5a1b3Sopenharmony_ci    }
97553a5a1b3Sopenharmony_ci
97653a5a1b3Sopenharmony_ci    pa_gettimeofday(&now);
97753a5a1b3Sopenharmony_ci
97853a5a1b3Sopenharmony_ci    /* Calculate transport usec */
97953a5a1b3Sopenharmony_ci    if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now) < 0) {
98053a5a1b3Sopenharmony_ci        /* local and remote seem to have synchronized clocks */
98153a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
98253a5a1b3Sopenharmony_ci        u->transport_usec = pa_timeval_diff(&remote, &local);
98353a5a1b3Sopenharmony_ci#else
98453a5a1b3Sopenharmony_ci        u->transport_usec = pa_timeval_diff(&now, &remote);
98553a5a1b3Sopenharmony_ci#endif
98653a5a1b3Sopenharmony_ci    } else
98753a5a1b3Sopenharmony_ci        u->transport_usec = pa_timeval_diff(&now, &local)/2;
98853a5a1b3Sopenharmony_ci
98953a5a1b3Sopenharmony_ci    /* First, take the device's delay */
99053a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
99153a5a1b3Sopenharmony_ci    delay = (int64_t) sink_usec;
99253a5a1b3Sopenharmony_ci    ss = &u->sink->sample_spec;
99353a5a1b3Sopenharmony_ci#else
99453a5a1b3Sopenharmony_ci    delay = (int64_t) source_usec;
99553a5a1b3Sopenharmony_ci    ss = &u->source->sample_spec;
99653a5a1b3Sopenharmony_ci#endif
99753a5a1b3Sopenharmony_ci
99853a5a1b3Sopenharmony_ci    /* Add the length of our server-side buffer */
99953a5a1b3Sopenharmony_ci    if (write_index >= read_index)
100053a5a1b3Sopenharmony_ci        delay += (int64_t) pa_bytes_to_usec((uint64_t) (write_index-read_index), ss);
100153a5a1b3Sopenharmony_ci    else
100253a5a1b3Sopenharmony_ci        delay -= (int64_t) pa_bytes_to_usec((uint64_t) (read_index-write_index), ss);
100353a5a1b3Sopenharmony_ci
100453a5a1b3Sopenharmony_ci    /* Our measurements are already out of date, hence correct by the     *
100553a5a1b3Sopenharmony_ci     * transport latency */
100653a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
100753a5a1b3Sopenharmony_ci    delay -= (int64_t) u->transport_usec;
100853a5a1b3Sopenharmony_ci#else
100953a5a1b3Sopenharmony_ci    delay += (int64_t) u->transport_usec;
101053a5a1b3Sopenharmony_ci#endif
101153a5a1b3Sopenharmony_ci
101253a5a1b3Sopenharmony_ci    /* Now correct by what we have have written since we requested the update. This
101353a5a1b3Sopenharmony_ci     * is not necessary for the source, because if data is received between request
101453a5a1b3Sopenharmony_ci     * and reply, it was already posted before we requested the source latency. */
101553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
101653a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_GET_LATENCY_SNAPSHOT, &send_counter, 0, NULL);
101753a5a1b3Sopenharmony_ci    delay += (int64_t) pa_bytes_to_usec(send_counter - u->receive_snapshot, ss);
101853a5a1b3Sopenharmony_ci#endif
101953a5a1b3Sopenharmony_ci
102053a5a1b3Sopenharmony_ci    /* It may take some time before the async message is executed, so we take a timestamp here */
102153a5a1b3Sopenharmony_ci    u->snapshot_time = pa_rtclock_now();
102253a5a1b3Sopenharmony_ci
102353a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
102453a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
102553a5a1b3Sopenharmony_ci#else
102653a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
102753a5a1b3Sopenharmony_ci#endif
102853a5a1b3Sopenharmony_ci
102953a5a1b3Sopenharmony_ci    return;
103053a5a1b3Sopenharmony_ci
103153a5a1b3Sopenharmony_cifail:
103253a5a1b3Sopenharmony_ci
103353a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
103453a5a1b3Sopenharmony_ci}
103553a5a1b3Sopenharmony_ci
103653a5a1b3Sopenharmony_ci/* Called from main context */
103753a5a1b3Sopenharmony_cistatic void request_latency(struct userdata *u) {
103853a5a1b3Sopenharmony_ci    pa_tagstruct *t;
103953a5a1b3Sopenharmony_ci    struct timeval now;
104053a5a1b3Sopenharmony_ci    uint32_t tag;
104153a5a1b3Sopenharmony_ci    pa_assert(u);
104253a5a1b3Sopenharmony_ci
104353a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
104453a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
104553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
104653a5a1b3Sopenharmony_ci#else
104753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
104853a5a1b3Sopenharmony_ci#endif
104953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, tag = u->ctag++);
105053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->channel);
105153a5a1b3Sopenharmony_ci
105253a5a1b3Sopenharmony_ci    pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
105353a5a1b3Sopenharmony_ci
105453a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
105553a5a1b3Sopenharmony_ci    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
105653a5a1b3Sopenharmony_ci
105753a5a1b3Sopenharmony_ci    u->ignore_latency_before = tag;
105853a5a1b3Sopenharmony_ci    u->receive_snapshot = u->receive_counter;
105953a5a1b3Sopenharmony_ci}
106053a5a1b3Sopenharmony_ci
106153a5a1b3Sopenharmony_ci/* Called from main context */
106253a5a1b3Sopenharmony_cistatic void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
106353a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
106453a5a1b3Sopenharmony_ci
106553a5a1b3Sopenharmony_ci    pa_assert(m);
106653a5a1b3Sopenharmony_ci    pa_assert(e);
106753a5a1b3Sopenharmony_ci    pa_assert(u);
106853a5a1b3Sopenharmony_ci
106953a5a1b3Sopenharmony_ci    request_latency(u);
107053a5a1b3Sopenharmony_ci
107153a5a1b3Sopenharmony_ci    pa_core_rttime_restart(u->core, e, pa_rtclock_now() + LATENCY_INTERVAL);
107253a5a1b3Sopenharmony_ci}
107353a5a1b3Sopenharmony_ci
107453a5a1b3Sopenharmony_ci/* Called from main context */
107553a5a1b3Sopenharmony_cistatic void update_description(struct userdata *u) {
107653a5a1b3Sopenharmony_ci    char *d;
107753a5a1b3Sopenharmony_ci    char un[128], hn[128];
107853a5a1b3Sopenharmony_ci    pa_tagstruct *t;
107953a5a1b3Sopenharmony_ci
108053a5a1b3Sopenharmony_ci    pa_assert(u);
108153a5a1b3Sopenharmony_ci
108253a5a1b3Sopenharmony_ci    if (!u->server_fqdn || !u->user_name || !u->device_description)
108353a5a1b3Sopenharmony_ci        return;
108453a5a1b3Sopenharmony_ci
108553a5a1b3Sopenharmony_ci    d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
108653a5a1b3Sopenharmony_ci
108753a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
108853a5a1b3Sopenharmony_ci    pa_sink_set_description(u->sink, d);
108953a5a1b3Sopenharmony_ci    pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name);
109053a5a1b3Sopenharmony_ci    pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn);
109153a5a1b3Sopenharmony_ci    pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description);
109253a5a1b3Sopenharmony_ci#else
109353a5a1b3Sopenharmony_ci    pa_source_set_description(u->source, d);
109453a5a1b3Sopenharmony_ci    pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name);
109553a5a1b3Sopenharmony_ci    pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn);
109653a5a1b3Sopenharmony_ci    pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description);
109753a5a1b3Sopenharmony_ci#endif
109853a5a1b3Sopenharmony_ci
109953a5a1b3Sopenharmony_ci    pa_xfree(d);
110053a5a1b3Sopenharmony_ci
110153a5a1b3Sopenharmony_ci    d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
110253a5a1b3Sopenharmony_ci                          pa_get_user_name(un, sizeof(un)),
110353a5a1b3Sopenharmony_ci                          pa_get_host_name(hn, sizeof(hn)));
110453a5a1b3Sopenharmony_ci
110553a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
110653a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
110753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
110853a5a1b3Sopenharmony_ci#else
110953a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
111053a5a1b3Sopenharmony_ci#endif
111153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->ctag++);
111253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->channel);
111353a5a1b3Sopenharmony_ci    pa_tagstruct_puts(t, d);
111453a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
111553a5a1b3Sopenharmony_ci
111653a5a1b3Sopenharmony_ci    pa_xfree(d);
111753a5a1b3Sopenharmony_ci}
111853a5a1b3Sopenharmony_ci
111953a5a1b3Sopenharmony_ci/* Called from main context */
112053a5a1b3Sopenharmony_cistatic void server_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
112153a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
112253a5a1b3Sopenharmony_ci    pa_sample_spec ss;
112353a5a1b3Sopenharmony_ci    pa_channel_map cm;
112453a5a1b3Sopenharmony_ci    const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
112553a5a1b3Sopenharmony_ci    uint32_t cookie;
112653a5a1b3Sopenharmony_ci
112753a5a1b3Sopenharmony_ci    pa_assert(pd);
112853a5a1b3Sopenharmony_ci    pa_assert(u);
112953a5a1b3Sopenharmony_ci
113053a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY) {
113153a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
113253a5a1b3Sopenharmony_ci            pa_log("Failed to get info.");
113353a5a1b3Sopenharmony_ci        else
113453a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
113553a5a1b3Sopenharmony_ci        goto fail;
113653a5a1b3Sopenharmony_ci    }
113753a5a1b3Sopenharmony_ci
113853a5a1b3Sopenharmony_ci    if (pa_tagstruct_gets(t, &server_name) < 0 ||
113953a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &server_version) < 0 ||
114053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &user_name) < 0 ||
114153a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &host_name) < 0 ||
114253a5a1b3Sopenharmony_ci        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
114353a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &default_sink_name) < 0 ||
114453a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &default_source_name) < 0 ||
114553a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &cookie) < 0 ||
114653a5a1b3Sopenharmony_ci        (u->version >= 15 && pa_tagstruct_get_channel_map(t, &cm) < 0)) {
114753a5a1b3Sopenharmony_ci
114853a5a1b3Sopenharmony_ci        pa_log("Parse failure");
114953a5a1b3Sopenharmony_ci        goto fail;
115053a5a1b3Sopenharmony_ci    }
115153a5a1b3Sopenharmony_ci
115253a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
115353a5a1b3Sopenharmony_ci        pa_log("Packet too long");
115453a5a1b3Sopenharmony_ci        goto fail;
115553a5a1b3Sopenharmony_ci    }
115653a5a1b3Sopenharmony_ci
115753a5a1b3Sopenharmony_ci    pa_xfree(u->server_fqdn);
115853a5a1b3Sopenharmony_ci    u->server_fqdn = pa_xstrdup(host_name);
115953a5a1b3Sopenharmony_ci
116053a5a1b3Sopenharmony_ci    pa_xfree(u->user_name);
116153a5a1b3Sopenharmony_ci    u->user_name = pa_xstrdup(user_name);
116253a5a1b3Sopenharmony_ci
116353a5a1b3Sopenharmony_ci    update_description(u);
116453a5a1b3Sopenharmony_ci
116553a5a1b3Sopenharmony_ci    return;
116653a5a1b3Sopenharmony_ci
116753a5a1b3Sopenharmony_cifail:
116853a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
116953a5a1b3Sopenharmony_ci}
117053a5a1b3Sopenharmony_ci
117153a5a1b3Sopenharmony_cistatic int read_ports(struct userdata *u, pa_tagstruct *t) {
117253a5a1b3Sopenharmony_ci    if (u->version >= 16) {
117353a5a1b3Sopenharmony_ci        uint32_t n_ports;
117453a5a1b3Sopenharmony_ci        const char *s;
117553a5a1b3Sopenharmony_ci
117653a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &n_ports)) {
117753a5a1b3Sopenharmony_ci            pa_log("Parse failure");
117853a5a1b3Sopenharmony_ci            return -PA_ERR_PROTOCOL;
117953a5a1b3Sopenharmony_ci        }
118053a5a1b3Sopenharmony_ci
118153a5a1b3Sopenharmony_ci        for (uint32_t j = 0; j < n_ports; j++) {
118253a5a1b3Sopenharmony_ci            uint32_t priority;
118353a5a1b3Sopenharmony_ci
118453a5a1b3Sopenharmony_ci            if (pa_tagstruct_gets(t, &s) < 0 || /* name */
118553a5a1b3Sopenharmony_ci                pa_tagstruct_gets(t, &s) < 0 || /* description */
118653a5a1b3Sopenharmony_ci                pa_tagstruct_getu32(t, &priority) < 0) {
118753a5a1b3Sopenharmony_ci
118853a5a1b3Sopenharmony_ci                pa_log("Parse failure");
118953a5a1b3Sopenharmony_ci                return -PA_ERR_PROTOCOL;
119053a5a1b3Sopenharmony_ci            }
119153a5a1b3Sopenharmony_ci            if (u->version >= 24) {
119253a5a1b3Sopenharmony_ci                if (pa_tagstruct_getu32(t, &priority) < 0) { /* available */
119353a5a1b3Sopenharmony_ci                    pa_log("Parse failure");
119453a5a1b3Sopenharmony_ci                    return -PA_ERR_PROTOCOL;
119553a5a1b3Sopenharmony_ci                }
119653a5a1b3Sopenharmony_ci                if (u->version >= 34 &&
119753a5a1b3Sopenharmony_ci                    (pa_tagstruct_gets(t, &s) < 0 || /* availability group */
119853a5a1b3Sopenharmony_ci                     pa_tagstruct_getu32(t, &priority) < 0)) { /* device port type */
119953a5a1b3Sopenharmony_ci                    pa_log("Parse failure");
120053a5a1b3Sopenharmony_ci                    return -PA_ERR_PROTOCOL;
120153a5a1b3Sopenharmony_ci                }
120253a5a1b3Sopenharmony_ci            }
120353a5a1b3Sopenharmony_ci        }
120453a5a1b3Sopenharmony_ci
120553a5a1b3Sopenharmony_ci        if (pa_tagstruct_gets(t, &s) < 0) { /* active port */
120653a5a1b3Sopenharmony_ci            pa_log("Parse failure");
120753a5a1b3Sopenharmony_ci            return -PA_ERR_PROTOCOL;
120853a5a1b3Sopenharmony_ci        }
120953a5a1b3Sopenharmony_ci    }
121053a5a1b3Sopenharmony_ci    return 0;
121153a5a1b3Sopenharmony_ci}
121253a5a1b3Sopenharmony_ci
121353a5a1b3Sopenharmony_cistatic int read_formats(struct userdata *u, pa_tagstruct *t) {
121453a5a1b3Sopenharmony_ci    uint8_t n_formats;
121553a5a1b3Sopenharmony_ci    pa_format_info *format;
121653a5a1b3Sopenharmony_ci
121753a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu8(t, &n_formats) < 0) { /* no. of formats */
121853a5a1b3Sopenharmony_ci        pa_log("Parse failure");
121953a5a1b3Sopenharmony_ci        return -PA_ERR_PROTOCOL;
122053a5a1b3Sopenharmony_ci    }
122153a5a1b3Sopenharmony_ci
122253a5a1b3Sopenharmony_ci    for (uint8_t j = 0; j < n_formats; j++) {
122353a5a1b3Sopenharmony_ci        format = pa_format_info_new();
122453a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_format_info(t, format)) { /* format info */
122553a5a1b3Sopenharmony_ci            pa_format_info_free(format);
122653a5a1b3Sopenharmony_ci            pa_log("Parse failure");
122753a5a1b3Sopenharmony_ci            return -PA_ERR_PROTOCOL;
122853a5a1b3Sopenharmony_ci        }
122953a5a1b3Sopenharmony_ci        pa_format_info_free(format);
123053a5a1b3Sopenharmony_ci    }
123153a5a1b3Sopenharmony_ci    return 0;
123253a5a1b3Sopenharmony_ci}
123353a5a1b3Sopenharmony_ci
123453a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
123553a5a1b3Sopenharmony_ci
123653a5a1b3Sopenharmony_ci/* Called from main context */
123753a5a1b3Sopenharmony_cistatic void sink_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
123853a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
123953a5a1b3Sopenharmony_ci    uint32_t idx, owner_module, monitor_source, flags;
124053a5a1b3Sopenharmony_ci    const char *name, *description, *monitor_source_name, *driver;
124153a5a1b3Sopenharmony_ci    pa_sample_spec ss;
124253a5a1b3Sopenharmony_ci    pa_channel_map cm;
124353a5a1b3Sopenharmony_ci    pa_cvolume volume;
124453a5a1b3Sopenharmony_ci    bool mute;
124553a5a1b3Sopenharmony_ci    pa_usec_t latency;
124653a5a1b3Sopenharmony_ci
124753a5a1b3Sopenharmony_ci    pa_assert(pd);
124853a5a1b3Sopenharmony_ci    pa_assert(u);
124953a5a1b3Sopenharmony_ci
125053a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY) {
125153a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
125253a5a1b3Sopenharmony_ci            pa_log("Failed to get info.");
125353a5a1b3Sopenharmony_ci        else
125453a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
125553a5a1b3Sopenharmony_ci        goto fail;
125653a5a1b3Sopenharmony_ci    }
125753a5a1b3Sopenharmony_ci
125853a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
125953a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
126053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &description) < 0 ||
126153a5a1b3Sopenharmony_ci        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
126253a5a1b3Sopenharmony_ci        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
126353a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &owner_module) < 0 ||
126453a5a1b3Sopenharmony_ci        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
126553a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &mute) < 0 ||
126653a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &monitor_source) < 0 ||
126753a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
126853a5a1b3Sopenharmony_ci        pa_tagstruct_get_usec(t, &latency) < 0 ||
126953a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &driver) < 0 ||
127053a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &flags) < 0) {
127153a5a1b3Sopenharmony_ci
127253a5a1b3Sopenharmony_ci        pa_log("Parse failure");
127353a5a1b3Sopenharmony_ci        goto fail;
127453a5a1b3Sopenharmony_ci    }
127553a5a1b3Sopenharmony_ci
127653a5a1b3Sopenharmony_ci    if (u->version >= 13) {
127753a5a1b3Sopenharmony_ci        pa_usec_t configured_latency;
127853a5a1b3Sopenharmony_ci
127953a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
128053a5a1b3Sopenharmony_ci            pa_tagstruct_get_usec(t, &configured_latency) < 0) {
128153a5a1b3Sopenharmony_ci
128253a5a1b3Sopenharmony_ci            pa_log("Parse failure");
128353a5a1b3Sopenharmony_ci            goto fail;
128453a5a1b3Sopenharmony_ci        }
128553a5a1b3Sopenharmony_ci    }
128653a5a1b3Sopenharmony_ci
128753a5a1b3Sopenharmony_ci    if (u->version >= 15) {
128853a5a1b3Sopenharmony_ci        pa_volume_t base_volume;
128953a5a1b3Sopenharmony_ci        uint32_t state, n_volume_steps, card;
129053a5a1b3Sopenharmony_ci
129153a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
129253a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &state) < 0 ||
129353a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
129453a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &card) < 0) {
129553a5a1b3Sopenharmony_ci
129653a5a1b3Sopenharmony_ci            pa_log("Parse failure");
129753a5a1b3Sopenharmony_ci            goto fail;
129853a5a1b3Sopenharmony_ci        }
129953a5a1b3Sopenharmony_ci    }
130053a5a1b3Sopenharmony_ci
130153a5a1b3Sopenharmony_ci    if (read_ports(u, t) < 0)
130253a5a1b3Sopenharmony_ci        goto fail;
130353a5a1b3Sopenharmony_ci
130453a5a1b3Sopenharmony_ci    if (u->version >= 21 && read_formats(u, t) < 0)
130553a5a1b3Sopenharmony_ci        goto fail;
130653a5a1b3Sopenharmony_ci
130753a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
130853a5a1b3Sopenharmony_ci        pa_log("Packet too long");
130953a5a1b3Sopenharmony_ci        goto fail;
131053a5a1b3Sopenharmony_ci    }
131153a5a1b3Sopenharmony_ci
131253a5a1b3Sopenharmony_ci    if (!u->sink_name || !pa_streq(name, u->sink_name))
131353a5a1b3Sopenharmony_ci        return;
131453a5a1b3Sopenharmony_ci
131553a5a1b3Sopenharmony_ci    pa_xfree(u->device_description);
131653a5a1b3Sopenharmony_ci    u->device_description = pa_xstrdup(description);
131753a5a1b3Sopenharmony_ci
131853a5a1b3Sopenharmony_ci    update_description(u);
131953a5a1b3Sopenharmony_ci
132053a5a1b3Sopenharmony_ci    return;
132153a5a1b3Sopenharmony_ci
132253a5a1b3Sopenharmony_cifail:
132353a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
132453a5a1b3Sopenharmony_ci}
132553a5a1b3Sopenharmony_ci
132653a5a1b3Sopenharmony_ci/* Called from main context */
132753a5a1b3Sopenharmony_cistatic void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
132853a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
132953a5a1b3Sopenharmony_ci    uint32_t idx, owner_module, client, sink;
133053a5a1b3Sopenharmony_ci    pa_usec_t buffer_usec, sink_usec;
133153a5a1b3Sopenharmony_ci    const char *name, *driver, *resample_method;
133253a5a1b3Sopenharmony_ci    bool mute = false;
133353a5a1b3Sopenharmony_ci    pa_sample_spec sample_spec;
133453a5a1b3Sopenharmony_ci    pa_channel_map channel_map;
133553a5a1b3Sopenharmony_ci    pa_cvolume volume;
133653a5a1b3Sopenharmony_ci    bool b;
133753a5a1b3Sopenharmony_ci
133853a5a1b3Sopenharmony_ci    pa_assert(pd);
133953a5a1b3Sopenharmony_ci    pa_assert(u);
134053a5a1b3Sopenharmony_ci
134153a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY) {
134253a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
134353a5a1b3Sopenharmony_ci            pa_log("Failed to get info.");
134453a5a1b3Sopenharmony_ci        else
134553a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
134653a5a1b3Sopenharmony_ci        goto fail;
134753a5a1b3Sopenharmony_ci    }
134853a5a1b3Sopenharmony_ci
134953a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
135053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
135153a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &owner_module) < 0 ||
135253a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &client) < 0 ||
135353a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &sink) < 0 ||
135453a5a1b3Sopenharmony_ci        pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
135553a5a1b3Sopenharmony_ci        pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
135653a5a1b3Sopenharmony_ci        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
135753a5a1b3Sopenharmony_ci        pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
135853a5a1b3Sopenharmony_ci        pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
135953a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &resample_method) < 0 ||
136053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &driver) < 0) {
136153a5a1b3Sopenharmony_ci
136253a5a1b3Sopenharmony_ci        pa_log("Parse failure");
136353a5a1b3Sopenharmony_ci        goto fail;
136453a5a1b3Sopenharmony_ci    }
136553a5a1b3Sopenharmony_ci
136653a5a1b3Sopenharmony_ci    if (u->version >= 11) {
136753a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &mute) < 0) {
136853a5a1b3Sopenharmony_ci
136953a5a1b3Sopenharmony_ci            pa_log("Parse failure");
137053a5a1b3Sopenharmony_ci            goto fail;
137153a5a1b3Sopenharmony_ci        }
137253a5a1b3Sopenharmony_ci    }
137353a5a1b3Sopenharmony_ci
137453a5a1b3Sopenharmony_ci    if (u->version >= 13) {
137553a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_proplist(t, NULL) < 0) {
137653a5a1b3Sopenharmony_ci
137753a5a1b3Sopenharmony_ci            pa_log("Parse failure");
137853a5a1b3Sopenharmony_ci            goto fail;
137953a5a1b3Sopenharmony_ci        }
138053a5a1b3Sopenharmony_ci    }
138153a5a1b3Sopenharmony_ci
138253a5a1b3Sopenharmony_ci    if (u->version >= 19) {
138353a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &b) < 0) {
138453a5a1b3Sopenharmony_ci
138553a5a1b3Sopenharmony_ci            pa_log("Parse failure");
138653a5a1b3Sopenharmony_ci            goto fail;
138753a5a1b3Sopenharmony_ci        }
138853a5a1b3Sopenharmony_ci    }
138953a5a1b3Sopenharmony_ci
139053a5a1b3Sopenharmony_ci    if (u->version >= 20) {
139153a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_boolean(t, &b) < 0 ||
139253a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &b) < 0) {
139353a5a1b3Sopenharmony_ci
139453a5a1b3Sopenharmony_ci            pa_log("Parse failure");
139553a5a1b3Sopenharmony_ci            goto fail;
139653a5a1b3Sopenharmony_ci        }
139753a5a1b3Sopenharmony_ci    }
139853a5a1b3Sopenharmony_ci
139953a5a1b3Sopenharmony_ci    if (u->version >= 21) {
140053a5a1b3Sopenharmony_ci        pa_format_info *format = pa_format_info_new();
140153a5a1b3Sopenharmony_ci
140253a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_format_info(t, format) < 0) {
140353a5a1b3Sopenharmony_ci            pa_format_info_free(format);
140453a5a1b3Sopenharmony_ci            pa_log("Parse failure");
140553a5a1b3Sopenharmony_ci            goto fail;
140653a5a1b3Sopenharmony_ci        }
140753a5a1b3Sopenharmony_ci        pa_format_info_free(format);
140853a5a1b3Sopenharmony_ci    }
140953a5a1b3Sopenharmony_ci
141053a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
141153a5a1b3Sopenharmony_ci        pa_log("Packet too long");
141253a5a1b3Sopenharmony_ci        goto fail;
141353a5a1b3Sopenharmony_ci    }
141453a5a1b3Sopenharmony_ci
141553a5a1b3Sopenharmony_ci    if (idx != u->device_index)
141653a5a1b3Sopenharmony_ci        return;
141753a5a1b3Sopenharmony_ci
141853a5a1b3Sopenharmony_ci    pa_assert(u->sink);
141953a5a1b3Sopenharmony_ci
142053a5a1b3Sopenharmony_ci    if ((u->version < 11 || mute == u->sink->muted) &&
142153a5a1b3Sopenharmony_ci        pa_cvolume_equal(&volume, &u->sink->real_volume))
142253a5a1b3Sopenharmony_ci        return;
142353a5a1b3Sopenharmony_ci
142453a5a1b3Sopenharmony_ci    pa_sink_volume_changed(u->sink, &volume);
142553a5a1b3Sopenharmony_ci
142653a5a1b3Sopenharmony_ci    if (u->version >= 11)
142753a5a1b3Sopenharmony_ci        pa_sink_mute_changed(u->sink, mute);
142853a5a1b3Sopenharmony_ci
142953a5a1b3Sopenharmony_ci    return;
143053a5a1b3Sopenharmony_ci
143153a5a1b3Sopenharmony_cifail:
143253a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
143353a5a1b3Sopenharmony_ci}
143453a5a1b3Sopenharmony_ci
143553a5a1b3Sopenharmony_ci#else
143653a5a1b3Sopenharmony_ci
143753a5a1b3Sopenharmony_ci/* Called from main context */
143853a5a1b3Sopenharmony_cistatic void source_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
143953a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
144053a5a1b3Sopenharmony_ci    uint32_t idx, owner_module, monitor_of_sink, flags;
144153a5a1b3Sopenharmony_ci    const char *name, *description, *monitor_of_sink_name, *driver;
144253a5a1b3Sopenharmony_ci    pa_sample_spec ss;
144353a5a1b3Sopenharmony_ci    pa_channel_map cm;
144453a5a1b3Sopenharmony_ci    pa_cvolume volume;
144553a5a1b3Sopenharmony_ci    bool mute;
144653a5a1b3Sopenharmony_ci    pa_usec_t latency, configured_latency;
144753a5a1b3Sopenharmony_ci
144853a5a1b3Sopenharmony_ci    pa_assert(pd);
144953a5a1b3Sopenharmony_ci    pa_assert(u);
145053a5a1b3Sopenharmony_ci
145153a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY) {
145253a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
145353a5a1b3Sopenharmony_ci            pa_log("Failed to get info.");
145453a5a1b3Sopenharmony_ci        else
145553a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
145653a5a1b3Sopenharmony_ci        goto fail;
145753a5a1b3Sopenharmony_ci    }
145853a5a1b3Sopenharmony_ci
145953a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &idx) < 0 ||
146053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &name) < 0 ||
146153a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &description) < 0 ||
146253a5a1b3Sopenharmony_ci        pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
146353a5a1b3Sopenharmony_ci        pa_tagstruct_get_channel_map(t, &cm) < 0 ||
146453a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &owner_module) < 0 ||
146553a5a1b3Sopenharmony_ci        pa_tagstruct_get_cvolume(t, &volume) < 0 ||
146653a5a1b3Sopenharmony_ci        pa_tagstruct_get_boolean(t, &mute) < 0 ||
146753a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
146853a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
146953a5a1b3Sopenharmony_ci        pa_tagstruct_get_usec(t, &latency) < 0 ||
147053a5a1b3Sopenharmony_ci        pa_tagstruct_gets(t, &driver) < 0 ||
147153a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &flags) < 0) {
147253a5a1b3Sopenharmony_ci
147353a5a1b3Sopenharmony_ci        pa_log("Parse failure");
147453a5a1b3Sopenharmony_ci        goto fail;
147553a5a1b3Sopenharmony_ci    }
147653a5a1b3Sopenharmony_ci
147753a5a1b3Sopenharmony_ci    if (u->version >= 13) {
147853a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
147953a5a1b3Sopenharmony_ci            pa_tagstruct_get_usec(t, &configured_latency) < 0) {
148053a5a1b3Sopenharmony_ci
148153a5a1b3Sopenharmony_ci            pa_log("Parse failure");
148253a5a1b3Sopenharmony_ci            goto fail;
148353a5a1b3Sopenharmony_ci        }
148453a5a1b3Sopenharmony_ci    }
148553a5a1b3Sopenharmony_ci
148653a5a1b3Sopenharmony_ci    if (u->version >= 15) {
148753a5a1b3Sopenharmony_ci        pa_volume_t base_volume;
148853a5a1b3Sopenharmony_ci        uint32_t state, n_volume_steps, card;
148953a5a1b3Sopenharmony_ci
149053a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
149153a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &state) < 0 ||
149253a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
149353a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &card) < 0) {
149453a5a1b3Sopenharmony_ci
149553a5a1b3Sopenharmony_ci            pa_log("Parse failure");
149653a5a1b3Sopenharmony_ci            goto fail;
149753a5a1b3Sopenharmony_ci        }
149853a5a1b3Sopenharmony_ci    }
149953a5a1b3Sopenharmony_ci
150053a5a1b3Sopenharmony_ci    if (read_ports(u, t) < 0)
150153a5a1b3Sopenharmony_ci        goto fail;
150253a5a1b3Sopenharmony_ci
150353a5a1b3Sopenharmony_ci    if (u->version >= 22 && read_formats(u, t) < 0)
150453a5a1b3Sopenharmony_ci        goto fail;
150553a5a1b3Sopenharmony_ci
150653a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t)) {
150753a5a1b3Sopenharmony_ci        pa_log("Packet too long");
150853a5a1b3Sopenharmony_ci        goto fail;
150953a5a1b3Sopenharmony_ci    }
151053a5a1b3Sopenharmony_ci
151153a5a1b3Sopenharmony_ci    if (!u->source_name || !pa_streq(name, u->source_name))
151253a5a1b3Sopenharmony_ci        return;
151353a5a1b3Sopenharmony_ci
151453a5a1b3Sopenharmony_ci    pa_xfree(u->device_description);
151553a5a1b3Sopenharmony_ci    u->device_description = pa_xstrdup(description);
151653a5a1b3Sopenharmony_ci
151753a5a1b3Sopenharmony_ci    update_description(u);
151853a5a1b3Sopenharmony_ci
151953a5a1b3Sopenharmony_ci    return;
152053a5a1b3Sopenharmony_ci
152153a5a1b3Sopenharmony_cifail:
152253a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
152353a5a1b3Sopenharmony_ci}
152453a5a1b3Sopenharmony_ci
152553a5a1b3Sopenharmony_ci#endif
152653a5a1b3Sopenharmony_ci
152753a5a1b3Sopenharmony_ci/* Called from main context */
152853a5a1b3Sopenharmony_cistatic void request_info(struct userdata *u) {
152953a5a1b3Sopenharmony_ci    pa_tagstruct *t;
153053a5a1b3Sopenharmony_ci    uint32_t tag;
153153a5a1b3Sopenharmony_ci    pa_assert(u);
153253a5a1b3Sopenharmony_ci
153353a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
153453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
153553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, tag = u->ctag++);
153653a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
153753a5a1b3Sopenharmony_ci    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
153853a5a1b3Sopenharmony_ci
153953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
154053a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
154153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
154253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, tag = u->ctag++);
154353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->device_index);
154453a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
154553a5a1b3Sopenharmony_ci    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
154653a5a1b3Sopenharmony_ci
154753a5a1b3Sopenharmony_ci    if (u->sink_name) {
154853a5a1b3Sopenharmony_ci        t = pa_tagstruct_new();
154953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
155053a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, tag = u->ctag++);
155153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, PA_INVALID_INDEX);
155253a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, u->sink_name);
155353a5a1b3Sopenharmony_ci        pa_pstream_send_tagstruct(u->pstream, t);
155453a5a1b3Sopenharmony_ci        pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
155553a5a1b3Sopenharmony_ci    }
155653a5a1b3Sopenharmony_ci#else
155753a5a1b3Sopenharmony_ci    if (u->source_name) {
155853a5a1b3Sopenharmony_ci        t = pa_tagstruct_new();
155953a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
156053a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, tag = u->ctag++);
156153a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(t, PA_INVALID_INDEX);
156253a5a1b3Sopenharmony_ci        pa_tagstruct_puts(t, u->source_name);
156353a5a1b3Sopenharmony_ci        pa_pstream_send_tagstruct(u->pstream, t);
156453a5a1b3Sopenharmony_ci        pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
156553a5a1b3Sopenharmony_ci    }
156653a5a1b3Sopenharmony_ci#endif
156753a5a1b3Sopenharmony_ci}
156853a5a1b3Sopenharmony_ci
156953a5a1b3Sopenharmony_ci/* Called from main context */
157053a5a1b3Sopenharmony_cistatic void command_subscribe_event(pa_pdispatch *pd,  uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
157153a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
157253a5a1b3Sopenharmony_ci    pa_subscription_event_type_t e;
157353a5a1b3Sopenharmony_ci    uint32_t idx;
157453a5a1b3Sopenharmony_ci
157553a5a1b3Sopenharmony_ci    pa_assert(pd);
157653a5a1b3Sopenharmony_ci    pa_assert(t);
157753a5a1b3Sopenharmony_ci    pa_assert(u);
157853a5a1b3Sopenharmony_ci    pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
157953a5a1b3Sopenharmony_ci
158053a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &e) < 0 ||
158153a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &idx) < 0) {
158253a5a1b3Sopenharmony_ci        pa_log("Invalid protocol reply");
158353a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
158453a5a1b3Sopenharmony_ci        return;
158553a5a1b3Sopenharmony_ci    }
158653a5a1b3Sopenharmony_ci
158753a5a1b3Sopenharmony_ci    if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
158853a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
158953a5a1b3Sopenharmony_ci        e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
159053a5a1b3Sopenharmony_ci        e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
159153a5a1b3Sopenharmony_ci#else
159253a5a1b3Sopenharmony_ci        e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
159353a5a1b3Sopenharmony_ci#endif
159453a5a1b3Sopenharmony_ci        )
159553a5a1b3Sopenharmony_ci        return;
159653a5a1b3Sopenharmony_ci
159753a5a1b3Sopenharmony_ci    request_info(u);
159853a5a1b3Sopenharmony_ci}
159953a5a1b3Sopenharmony_ci
160053a5a1b3Sopenharmony_ci/* Called from main context */
160153a5a1b3Sopenharmony_cistatic void start_subscribe(struct userdata *u) {
160253a5a1b3Sopenharmony_ci    pa_tagstruct *t;
160353a5a1b3Sopenharmony_ci    pa_assert(u);
160453a5a1b3Sopenharmony_ci
160553a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
160653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
160753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->ctag++);
160853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
160953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
161053a5a1b3Sopenharmony_ci                        PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
161153a5a1b3Sopenharmony_ci#else
161253a5a1b3Sopenharmony_ci                        PA_SUBSCRIPTION_MASK_SOURCE
161353a5a1b3Sopenharmony_ci#endif
161453a5a1b3Sopenharmony_ci                        );
161553a5a1b3Sopenharmony_ci
161653a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
161753a5a1b3Sopenharmony_ci}
161853a5a1b3Sopenharmony_ci
161953a5a1b3Sopenharmony_ci/* Called from main context */
162053a5a1b3Sopenharmony_cistatic void create_stream_callback(pa_pdispatch *pd, uint32_t command,  uint32_t tag, pa_tagstruct *t, void *userdata) {
162153a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
162253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
162353a5a1b3Sopenharmony_ci    uint32_t bytes;
162453a5a1b3Sopenharmony_ci#endif
162553a5a1b3Sopenharmony_ci
162653a5a1b3Sopenharmony_ci    pa_assert(pd);
162753a5a1b3Sopenharmony_ci    pa_assert(u);
162853a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
162953a5a1b3Sopenharmony_ci
163053a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY) {
163153a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
163253a5a1b3Sopenharmony_ci            pa_log("Failed to create stream.");
163353a5a1b3Sopenharmony_ci        else
163453a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
163553a5a1b3Sopenharmony_ci        goto fail;
163653a5a1b3Sopenharmony_ci    }
163753a5a1b3Sopenharmony_ci
163853a5a1b3Sopenharmony_ci    if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
163953a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &u->device_index) < 0
164053a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
164153a5a1b3Sopenharmony_ci        || pa_tagstruct_getu32(t, &bytes) < 0
164253a5a1b3Sopenharmony_ci#endif
164353a5a1b3Sopenharmony_ci        )
164453a5a1b3Sopenharmony_ci        goto parse_error;
164553a5a1b3Sopenharmony_ci
164653a5a1b3Sopenharmony_ci    if (u->version >= 9) {
164753a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
164853a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
164953a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &u->tlength) < 0 ||
165053a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &u->prebuf) < 0 ||
165153a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &u->minreq) < 0)
165253a5a1b3Sopenharmony_ci            goto parse_error;
165353a5a1b3Sopenharmony_ci#else
165453a5a1b3Sopenharmony_ci        if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
165553a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &u->fragsize) < 0)
165653a5a1b3Sopenharmony_ci            goto parse_error;
165753a5a1b3Sopenharmony_ci#endif
165853a5a1b3Sopenharmony_ci    }
165953a5a1b3Sopenharmony_ci
166053a5a1b3Sopenharmony_ci    if (u->version >= 12) {
166153a5a1b3Sopenharmony_ci        pa_sample_spec ss;
166253a5a1b3Sopenharmony_ci        pa_channel_map cm;
166353a5a1b3Sopenharmony_ci        uint32_t device_index;
166453a5a1b3Sopenharmony_ci        const char *dn;
166553a5a1b3Sopenharmony_ci        bool suspended;
166653a5a1b3Sopenharmony_ci
166753a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
166853a5a1b3Sopenharmony_ci            pa_tagstruct_get_channel_map(t, &cm) < 0 ||
166953a5a1b3Sopenharmony_ci            pa_tagstruct_getu32(t, &device_index) < 0 ||
167053a5a1b3Sopenharmony_ci            pa_tagstruct_gets(t, &dn) < 0 ||
167153a5a1b3Sopenharmony_ci            pa_tagstruct_get_boolean(t, &suspended) < 0)
167253a5a1b3Sopenharmony_ci            goto parse_error;
167353a5a1b3Sopenharmony_ci
167453a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
167553a5a1b3Sopenharmony_ci        pa_xfree(u->sink_name);
167653a5a1b3Sopenharmony_ci        u->sink_name = pa_xstrdup(dn);
167753a5a1b3Sopenharmony_ci#else
167853a5a1b3Sopenharmony_ci        pa_xfree(u->source_name);
167953a5a1b3Sopenharmony_ci        u->source_name = pa_xstrdup(dn);
168053a5a1b3Sopenharmony_ci#endif
168153a5a1b3Sopenharmony_ci    }
168253a5a1b3Sopenharmony_ci
168353a5a1b3Sopenharmony_ci    if (u->version >= 13) {
168453a5a1b3Sopenharmony_ci        pa_usec_t usec;
168553a5a1b3Sopenharmony_ci
168653a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_usec(t, &usec) < 0)
168753a5a1b3Sopenharmony_ci            goto parse_error;
168853a5a1b3Sopenharmony_ci
168953a5a1b3Sopenharmony_ci/* #ifdef TUNNEL_SINK */
169053a5a1b3Sopenharmony_ci/*         pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); */
169153a5a1b3Sopenharmony_ci/* #else */
169253a5a1b3Sopenharmony_ci/*         pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); */
169353a5a1b3Sopenharmony_ci/* #endif */
169453a5a1b3Sopenharmony_ci    }
169553a5a1b3Sopenharmony_ci
169653a5a1b3Sopenharmony_ci    if (u->version >= 21) {
169753a5a1b3Sopenharmony_ci        pa_format_info *format = pa_format_info_new();
169853a5a1b3Sopenharmony_ci
169953a5a1b3Sopenharmony_ci        if (pa_tagstruct_get_format_info(t, format) < 0) {
170053a5a1b3Sopenharmony_ci            pa_format_info_free(format);
170153a5a1b3Sopenharmony_ci            goto parse_error;
170253a5a1b3Sopenharmony_ci        }
170353a5a1b3Sopenharmony_ci
170453a5a1b3Sopenharmony_ci        pa_format_info_free(format);
170553a5a1b3Sopenharmony_ci    }
170653a5a1b3Sopenharmony_ci
170753a5a1b3Sopenharmony_ci    if (!pa_tagstruct_eof(t))
170853a5a1b3Sopenharmony_ci        goto parse_error;
170953a5a1b3Sopenharmony_ci
171053a5a1b3Sopenharmony_ci    start_subscribe(u);
171153a5a1b3Sopenharmony_ci    request_info(u);
171253a5a1b3Sopenharmony_ci
171353a5a1b3Sopenharmony_ci    pa_assert(!u->time_event);
171453a5a1b3Sopenharmony_ci    u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + LATENCY_INTERVAL, timeout_callback, u);
171553a5a1b3Sopenharmony_ci
171653a5a1b3Sopenharmony_ci    request_latency(u);
171753a5a1b3Sopenharmony_ci
171853a5a1b3Sopenharmony_ci    pa_log_debug("Stream created.");
171953a5a1b3Sopenharmony_ci
172053a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
172153a5a1b3Sopenharmony_ci    pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
172253a5a1b3Sopenharmony_ci#endif
172353a5a1b3Sopenharmony_ci
172453a5a1b3Sopenharmony_ci    return;
172553a5a1b3Sopenharmony_ci
172653a5a1b3Sopenharmony_ciparse_error:
172753a5a1b3Sopenharmony_ci    pa_log("Invalid reply. (Create stream)");
172853a5a1b3Sopenharmony_ci
172953a5a1b3Sopenharmony_cifail:
173053a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
173153a5a1b3Sopenharmony_ci
173253a5a1b3Sopenharmony_ci}
173353a5a1b3Sopenharmony_ci
173453a5a1b3Sopenharmony_ci/* Called from main context */
173553a5a1b3Sopenharmony_cistatic void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
173653a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
173753a5a1b3Sopenharmony_ci    pa_tagstruct *reply;
173853a5a1b3Sopenharmony_ci    char name[256], un[128], hn[128];
173953a5a1b3Sopenharmony_ci    pa_cvolume volume;
174053a5a1b3Sopenharmony_ci
174153a5a1b3Sopenharmony_ci    pa_assert(pd);
174253a5a1b3Sopenharmony_ci    pa_assert(u);
174353a5a1b3Sopenharmony_ci    pa_assert(u->pdispatch == pd);
174453a5a1b3Sopenharmony_ci
174553a5a1b3Sopenharmony_ci    if (command != PA_COMMAND_REPLY ||
174653a5a1b3Sopenharmony_ci        pa_tagstruct_getu32(t, &u->version) < 0 ||
174753a5a1b3Sopenharmony_ci        !pa_tagstruct_eof(t)) {
174853a5a1b3Sopenharmony_ci
174953a5a1b3Sopenharmony_ci        if (command == PA_COMMAND_ERROR)
175053a5a1b3Sopenharmony_ci            pa_log("Failed to authenticate");
175153a5a1b3Sopenharmony_ci        else
175253a5a1b3Sopenharmony_ci            pa_log("Protocol error.");
175353a5a1b3Sopenharmony_ci
175453a5a1b3Sopenharmony_ci        goto fail;
175553a5a1b3Sopenharmony_ci    }
175653a5a1b3Sopenharmony_ci
175753a5a1b3Sopenharmony_ci    /* Minimum supported protocol version */
175853a5a1b3Sopenharmony_ci    if (u->version < 8) {
175953a5a1b3Sopenharmony_ci        pa_log("Incompatible protocol version");
176053a5a1b3Sopenharmony_ci        goto fail;
176153a5a1b3Sopenharmony_ci    }
176253a5a1b3Sopenharmony_ci
176353a5a1b3Sopenharmony_ci    /* Starting with protocol version 13 the MSB of the version tag
176453a5a1b3Sopenharmony_ci    reflects if shm is enabled for this connection or not. We don't
176553a5a1b3Sopenharmony_ci    support SHM here at all, so we just ignore this. */
176653a5a1b3Sopenharmony_ci
176753a5a1b3Sopenharmony_ci    if (u->version >= 13)
176853a5a1b3Sopenharmony_ci        u->version &= 0x7FFFFFFFU;
176953a5a1b3Sopenharmony_ci
177053a5a1b3Sopenharmony_ci    pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
177153a5a1b3Sopenharmony_ci
177253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
177353a5a1b3Sopenharmony_ci    pa_proplist_setf(u->sink->proplist, "tunnel.remote_version", "%u", u->version);
177453a5a1b3Sopenharmony_ci    pa_sink_update_proplist(u->sink, 0, NULL);
177553a5a1b3Sopenharmony_ci
177653a5a1b3Sopenharmony_ci    pa_snprintf(name, sizeof(name), "%s for %s@%s",
177753a5a1b3Sopenharmony_ci                u->sink_name,
177853a5a1b3Sopenharmony_ci                pa_get_user_name(un, sizeof(un)),
177953a5a1b3Sopenharmony_ci                pa_get_host_name(hn, sizeof(hn)));
178053a5a1b3Sopenharmony_ci#else
178153a5a1b3Sopenharmony_ci    pa_proplist_setf(u->source->proplist, "tunnel.remote_version", "%u", u->version);
178253a5a1b3Sopenharmony_ci    pa_source_update_proplist(u->source, 0, NULL);
178353a5a1b3Sopenharmony_ci
178453a5a1b3Sopenharmony_ci    pa_snprintf(name, sizeof(name), "%s for %s@%s",
178553a5a1b3Sopenharmony_ci                u->source_name,
178653a5a1b3Sopenharmony_ci                pa_get_user_name(un, sizeof(un)),
178753a5a1b3Sopenharmony_ci                pa_get_host_name(hn, sizeof(hn)));
178853a5a1b3Sopenharmony_ci#endif
178953a5a1b3Sopenharmony_ci
179053a5a1b3Sopenharmony_ci    reply = pa_tagstruct_new();
179153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
179253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->ctag++);
179353a5a1b3Sopenharmony_ci
179453a5a1b3Sopenharmony_ci    if (u->version >= 13) {
179553a5a1b3Sopenharmony_ci        pa_proplist *pl;
179653a5a1b3Sopenharmony_ci        pl = pa_proplist_new();
179753a5a1b3Sopenharmony_ci        pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
179853a5a1b3Sopenharmony_ci        pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
179953a5a1b3Sopenharmony_ci        pa_init_proplist(pl);
180053a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(reply, pl);
180153a5a1b3Sopenharmony_ci        pa_proplist_free(pl);
180253a5a1b3Sopenharmony_ci    } else
180353a5a1b3Sopenharmony_ci        pa_tagstruct_puts(reply, "PulseAudio");
180453a5a1b3Sopenharmony_ci
180553a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, reply);
180653a5a1b3Sopenharmony_ci    /* We ignore the server's reply here */
180753a5a1b3Sopenharmony_ci
180853a5a1b3Sopenharmony_ci    reply = pa_tagstruct_new();
180953a5a1b3Sopenharmony_ci
181053a5a1b3Sopenharmony_ci    if (u->version < 13)
181153a5a1b3Sopenharmony_ci        /* Only for older PA versions we need to fill in the maxlength */
181253a5a1b3Sopenharmony_ci        u->maxlength = 4*1024*1024;
181353a5a1b3Sopenharmony_ci
181453a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
181553a5a1b3Sopenharmony_ci    u->tlength = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * u->latency, &u->sink->sample_spec);
181653a5a1b3Sopenharmony_ci    u->minreq = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * u->latency / 4, &u->sink->sample_spec);
181753a5a1b3Sopenharmony_ci    u->prebuf = u->tlength;
181853a5a1b3Sopenharmony_ci#else
181953a5a1b3Sopenharmony_ci    u->fragsize = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * u->latency, &u->source->sample_spec);
182053a5a1b3Sopenharmony_ci#endif
182153a5a1b3Sopenharmony_ci
182253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
182353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
182453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, tag = u->ctag++);
182553a5a1b3Sopenharmony_ci
182653a5a1b3Sopenharmony_ci    if (u->version < 13)
182753a5a1b3Sopenharmony_ci        pa_tagstruct_puts(reply, name);
182853a5a1b3Sopenharmony_ci
182953a5a1b3Sopenharmony_ci    pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
183053a5a1b3Sopenharmony_ci    pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
183153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
183253a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, u->sink_name);
183353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->maxlength);
183453a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(u->sink->state));
183553a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->tlength);
183653a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->prebuf);
183753a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->minreq);
183853a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, 0);
183953a5a1b3Sopenharmony_ci    pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
184053a5a1b3Sopenharmony_ci    pa_tagstruct_put_cvolume(reply, &volume);
184153a5a1b3Sopenharmony_ci#else
184253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
184353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, tag = u->ctag++);
184453a5a1b3Sopenharmony_ci
184553a5a1b3Sopenharmony_ci    if (u->version < 13)
184653a5a1b3Sopenharmony_ci        pa_tagstruct_puts(reply, name);
184753a5a1b3Sopenharmony_ci
184853a5a1b3Sopenharmony_ci    pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
184953a5a1b3Sopenharmony_ci    pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
185053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
185153a5a1b3Sopenharmony_ci    pa_tagstruct_puts(reply, u->source_name);
185253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->maxlength);
185353a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(u->source->state));
185453a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(reply, u->fragsize);
185553a5a1b3Sopenharmony_ci#endif
185653a5a1b3Sopenharmony_ci
185753a5a1b3Sopenharmony_ci    if (u->version >= 12) {
185853a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* no_remap */
185953a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* no_remix */
186053a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* fix_format */
186153a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* fix_rate */
186253a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* fix_channels */
186353a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, true); /* no_move */
186453a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* variable_rate */
186553a5a1b3Sopenharmony_ci    }
186653a5a1b3Sopenharmony_ci
186753a5a1b3Sopenharmony_ci    if (u->version >= 13) {
186853a5a1b3Sopenharmony_ci        pa_proplist *pl;
186953a5a1b3Sopenharmony_ci
187053a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* start muted/peak detect*/
187153a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, true); /* adjust_latency */
187253a5a1b3Sopenharmony_ci
187353a5a1b3Sopenharmony_ci        pl = pa_proplist_new();
187453a5a1b3Sopenharmony_ci        pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
187553a5a1b3Sopenharmony_ci        pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract");
187653a5a1b3Sopenharmony_ci        pa_tagstruct_put_proplist(reply, pl);
187753a5a1b3Sopenharmony_ci        pa_proplist_free(pl);
187853a5a1b3Sopenharmony_ci
187953a5a1b3Sopenharmony_ci#ifndef TUNNEL_SINK
188053a5a1b3Sopenharmony_ci        pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */
188153a5a1b3Sopenharmony_ci#endif
188253a5a1b3Sopenharmony_ci    }
188353a5a1b3Sopenharmony_ci
188453a5a1b3Sopenharmony_ci    if (u->version >= 14) {
188553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
188653a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* volume_set */
188753a5a1b3Sopenharmony_ci#endif
188853a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, true); /* early rquests */
188953a5a1b3Sopenharmony_ci    }
189053a5a1b3Sopenharmony_ci
189153a5a1b3Sopenharmony_ci    if (u->version >= 15) {
189253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
189353a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* muted_set */
189453a5a1b3Sopenharmony_ci#endif
189553a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* don't inhibit auto suspend */
189653a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* fail on suspend */
189753a5a1b3Sopenharmony_ci    }
189853a5a1b3Sopenharmony_ci
189953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
190053a5a1b3Sopenharmony_ci    if (u->version >= 17)
190153a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* relative volume */
190253a5a1b3Sopenharmony_ci
190353a5a1b3Sopenharmony_ci    if (u->version >= 18)
190453a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
190553a5a1b3Sopenharmony_ci#endif
190653a5a1b3Sopenharmony_ci
190753a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
190853a5a1b3Sopenharmony_ci    if (u->version >= 21) {
190953a5a1b3Sopenharmony_ci        /* We're not using the extended API, so n_formats = 0 and that's that */
191053a5a1b3Sopenharmony_ci        pa_tagstruct_putu8(reply, 0);
191153a5a1b3Sopenharmony_ci    }
191253a5a1b3Sopenharmony_ci#else
191353a5a1b3Sopenharmony_ci    if (u->version >= 22) {
191453a5a1b3Sopenharmony_ci        /* We're not using the extended API, so n_formats = 0 and that's that */
191553a5a1b3Sopenharmony_ci        pa_tagstruct_putu8(reply, 0);
191653a5a1b3Sopenharmony_ci        pa_cvolume_reset(&volume, u->source->sample_spec.channels);
191753a5a1b3Sopenharmony_ci        pa_tagstruct_put_cvolume(reply, &volume);
191853a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* muted */
191953a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* volume_set */
192053a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* muted_set */
192153a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* relative volume */
192253a5a1b3Sopenharmony_ci        pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
192353a5a1b3Sopenharmony_ci    }
192453a5a1b3Sopenharmony_ci#endif
192553a5a1b3Sopenharmony_ci
192653a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, reply);
192753a5a1b3Sopenharmony_ci    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
192853a5a1b3Sopenharmony_ci
192953a5a1b3Sopenharmony_ci    pa_log_debug("Connection authenticated, creating stream ...");
193053a5a1b3Sopenharmony_ci
193153a5a1b3Sopenharmony_ci    return;
193253a5a1b3Sopenharmony_ci
193353a5a1b3Sopenharmony_cifail:
193453a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
193553a5a1b3Sopenharmony_ci}
193653a5a1b3Sopenharmony_ci
193753a5a1b3Sopenharmony_ci/* Called from main context */
193853a5a1b3Sopenharmony_cistatic void pstream_die_callback(pa_pstream *p, void *userdata) {
193953a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
194053a5a1b3Sopenharmony_ci
194153a5a1b3Sopenharmony_ci    pa_assert(p);
194253a5a1b3Sopenharmony_ci    pa_assert(u);
194353a5a1b3Sopenharmony_ci
194453a5a1b3Sopenharmony_ci    pa_log_warn("Stream died.");
194553a5a1b3Sopenharmony_ci    unload_module(u->module->userdata);
194653a5a1b3Sopenharmony_ci}
194753a5a1b3Sopenharmony_ci
194853a5a1b3Sopenharmony_ci/* Called from main context */
194953a5a1b3Sopenharmony_cistatic void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
195053a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
195153a5a1b3Sopenharmony_ci
195253a5a1b3Sopenharmony_ci    pa_assert(p);
195353a5a1b3Sopenharmony_ci    pa_assert(packet);
195453a5a1b3Sopenharmony_ci    pa_assert(u);
195553a5a1b3Sopenharmony_ci
195653a5a1b3Sopenharmony_ci    if (pa_pdispatch_run(u->pdispatch, packet, ancil_data, u) < 0) {
195753a5a1b3Sopenharmony_ci        pa_log("Invalid packet");
195853a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
195953a5a1b3Sopenharmony_ci        return;
196053a5a1b3Sopenharmony_ci    }
196153a5a1b3Sopenharmony_ci}
196253a5a1b3Sopenharmony_ci
196353a5a1b3Sopenharmony_ci#ifndef TUNNEL_SINK
196453a5a1b3Sopenharmony_ci/* Called from main context */
196553a5a1b3Sopenharmony_cistatic void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
196653a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
196753a5a1b3Sopenharmony_ci
196853a5a1b3Sopenharmony_ci    pa_assert(p);
196953a5a1b3Sopenharmony_ci    pa_assert(chunk);
197053a5a1b3Sopenharmony_ci    pa_assert(u);
197153a5a1b3Sopenharmony_ci
197253a5a1b3Sopenharmony_ci    if (channel != u->channel) {
197353a5a1b3Sopenharmony_ci        pa_log("Received memory block on bad channel.");
197453a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
197553a5a1b3Sopenharmony_ci        return;
197653a5a1b3Sopenharmony_ci    }
197753a5a1b3Sopenharmony_ci
197853a5a1b3Sopenharmony_ci    pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk);
197953a5a1b3Sopenharmony_ci
198053a5a1b3Sopenharmony_ci    u->receive_counter += chunk->length;
198153a5a1b3Sopenharmony_ci}
198253a5a1b3Sopenharmony_ci#endif
198353a5a1b3Sopenharmony_ci
198453a5a1b3Sopenharmony_ci/* Called from main context */
198553a5a1b3Sopenharmony_cistatic void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
198653a5a1b3Sopenharmony_ci    struct userdata *u = userdata;
198753a5a1b3Sopenharmony_ci
198853a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
198953a5a1b3Sopenharmony_ci
199053a5a1b3Sopenharmony_ci    pa_assert(sc);
199153a5a1b3Sopenharmony_ci    pa_assert(u);
199253a5a1b3Sopenharmony_ci    pa_assert(u->client == sc);
199353a5a1b3Sopenharmony_ci
199453a5a1b3Sopenharmony_ci    pa_socket_client_unref(u->client);
199553a5a1b3Sopenharmony_ci    u->client = NULL;
199653a5a1b3Sopenharmony_ci
199753a5a1b3Sopenharmony_ci    if (!io) {
199853a5a1b3Sopenharmony_ci        pa_log("Connection failed: %s", pa_cstrerror(errno));
199953a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
200053a5a1b3Sopenharmony_ci        return;
200153a5a1b3Sopenharmony_ci    }
200253a5a1b3Sopenharmony_ci
200353a5a1b3Sopenharmony_ci    u->io = io;
200453a5a1b3Sopenharmony_ci
200553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
200653a5a1b3Sopenharmony_ci    create_sink(u);
200753a5a1b3Sopenharmony_ci    if (!u->sink) {
200853a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
200953a5a1b3Sopenharmony_ci        return;
201053a5a1b3Sopenharmony_ci    }
201153a5a1b3Sopenharmony_ci    on_sink_created(u);
201253a5a1b3Sopenharmony_ci#else
201353a5a1b3Sopenharmony_ci    create_source(u);
201453a5a1b3Sopenharmony_ci    if (!u->source) {
201553a5a1b3Sopenharmony_ci        unload_module(u->module->userdata);
201653a5a1b3Sopenharmony_ci        return;
201753a5a1b3Sopenharmony_ci    }
201853a5a1b3Sopenharmony_ci    on_source_created(u);
201953a5a1b3Sopenharmony_ci#endif
202053a5a1b3Sopenharmony_ci}
202153a5a1b3Sopenharmony_ci
202253a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
202353a5a1b3Sopenharmony_cistatic void on_sink_created(struct userdata *u)
202453a5a1b3Sopenharmony_ci#else
202553a5a1b3Sopenharmony_cistatic void on_source_created(struct userdata *u)
202653a5a1b3Sopenharmony_ci#endif
202753a5a1b3Sopenharmony_ci{
202853a5a1b3Sopenharmony_ci    pa_tagstruct *t;
202953a5a1b3Sopenharmony_ci    uint32_t tag;
203053a5a1b3Sopenharmony_ci
203153a5a1b3Sopenharmony_ci    u->pstream = pa_pstream_new(u->core->mainloop, u->io, u->core->mempool);
203253a5a1b3Sopenharmony_ci    u->pdispatch = pa_pdispatch_new(u->core->mainloop, true, command_table, PA_COMMAND_MAX);
203353a5a1b3Sopenharmony_ci
203453a5a1b3Sopenharmony_ci    pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
203553a5a1b3Sopenharmony_ci    pa_pstream_set_receive_packet_callback(u->pstream, pstream_packet_callback, u);
203653a5a1b3Sopenharmony_ci#ifndef TUNNEL_SINK
203753a5a1b3Sopenharmony_ci    pa_pstream_set_receive_memblock_callback(u->pstream, pstream_memblock_callback, u);
203853a5a1b3Sopenharmony_ci#endif
203953a5a1b3Sopenharmony_ci
204053a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
204153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
204253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, tag = u->ctag++);
204353a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
204453a5a1b3Sopenharmony_ci
204553a5a1b3Sopenharmony_ci    pa_tagstruct_put_arbitrary(t, pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH);
204653a5a1b3Sopenharmony_ci
204753a5a1b3Sopenharmony_ci#ifdef HAVE_CREDS
204853a5a1b3Sopenharmony_ci{
204953a5a1b3Sopenharmony_ci    pa_creds ucred;
205053a5a1b3Sopenharmony_ci
205153a5a1b3Sopenharmony_ci    if (pa_iochannel_creds_supported(u->io))
205253a5a1b3Sopenharmony_ci        pa_iochannel_creds_enable(u->io);
205353a5a1b3Sopenharmony_ci
205453a5a1b3Sopenharmony_ci    ucred.uid = getuid();
205553a5a1b3Sopenharmony_ci    ucred.gid = getgid();
205653a5a1b3Sopenharmony_ci
205753a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
205853a5a1b3Sopenharmony_ci}
205953a5a1b3Sopenharmony_ci#else
206053a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
206153a5a1b3Sopenharmony_ci#endif
206253a5a1b3Sopenharmony_ci
206353a5a1b3Sopenharmony_ci    pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
206453a5a1b3Sopenharmony_ci
206553a5a1b3Sopenharmony_ci    pa_log_debug("Connection established, authenticating ...");
206653a5a1b3Sopenharmony_ci}
206753a5a1b3Sopenharmony_ci
206853a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
206953a5a1b3Sopenharmony_ci
207053a5a1b3Sopenharmony_ci/* Called from main context */
207153a5a1b3Sopenharmony_cistatic void sink_set_volume(pa_sink *sink) {
207253a5a1b3Sopenharmony_ci    struct userdata *u;
207353a5a1b3Sopenharmony_ci    pa_tagstruct *t;
207453a5a1b3Sopenharmony_ci
207553a5a1b3Sopenharmony_ci    pa_assert(sink);
207653a5a1b3Sopenharmony_ci    u = sink->userdata;
207753a5a1b3Sopenharmony_ci    pa_assert(u);
207853a5a1b3Sopenharmony_ci
207953a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
208053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
208153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->ctag++);
208253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->device_index);
208353a5a1b3Sopenharmony_ci    pa_tagstruct_put_cvolume(t, &sink->real_volume);
208453a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
208553a5a1b3Sopenharmony_ci}
208653a5a1b3Sopenharmony_ci
208753a5a1b3Sopenharmony_ci/* Called from main context */
208853a5a1b3Sopenharmony_cistatic void sink_set_mute(pa_sink *sink) {
208953a5a1b3Sopenharmony_ci    struct userdata *u;
209053a5a1b3Sopenharmony_ci    pa_tagstruct *t;
209153a5a1b3Sopenharmony_ci
209253a5a1b3Sopenharmony_ci    pa_assert(sink);
209353a5a1b3Sopenharmony_ci    u = sink->userdata;
209453a5a1b3Sopenharmony_ci    pa_assert(u);
209553a5a1b3Sopenharmony_ci
209653a5a1b3Sopenharmony_ci    if (u->version < 11)
209753a5a1b3Sopenharmony_ci        return;
209853a5a1b3Sopenharmony_ci
209953a5a1b3Sopenharmony_ci    t = pa_tagstruct_new();
210053a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
210153a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->ctag++);
210253a5a1b3Sopenharmony_ci    pa_tagstruct_putu32(t, u->device_index);
210353a5a1b3Sopenharmony_ci    pa_tagstruct_put_boolean(t, sink->muted);
210453a5a1b3Sopenharmony_ci    pa_pstream_send_tagstruct(u->pstream, t);
210553a5a1b3Sopenharmony_ci}
210653a5a1b3Sopenharmony_ci
210753a5a1b3Sopenharmony_ci#endif
210853a5a1b3Sopenharmony_ci
210953a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
211053a5a1b3Sopenharmony_cistatic void create_sink(struct userdata *u) {
211153a5a1b3Sopenharmony_ci    pa_sink_new_data data;
211253a5a1b3Sopenharmony_ci    char *data_name = NULL;
211353a5a1b3Sopenharmony_ci
211453a5a1b3Sopenharmony_ci    if (!(data_name = pa_xstrdup(u->configured_sink_name)))
211553a5a1b3Sopenharmony_ci        data_name = pa_sprintf_malloc("tunnel-sink.%s", u->server_name);
211653a5a1b3Sopenharmony_ci
211753a5a1b3Sopenharmony_ci    pa_sink_new_data_init(&data);
211853a5a1b3Sopenharmony_ci    data.driver = __FILE__;
211953a5a1b3Sopenharmony_ci    data.module = u->module;
212053a5a1b3Sopenharmony_ci    data.namereg_fail = false;
212153a5a1b3Sopenharmony_ci    pa_sink_new_data_set_name(&data, data_name);
212253a5a1b3Sopenharmony_ci    pa_sink_new_data_set_sample_spec(&data, &u->sample_spec);
212353a5a1b3Sopenharmony_ci    pa_sink_new_data_set_channel_map(&data, &u->channel_map);
212453a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name);
212553a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
212653a5a1b3Sopenharmony_ci    if (u->sink_name)
212753a5a1b3Sopenharmony_ci        pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
212853a5a1b3Sopenharmony_ci
212953a5a1b3Sopenharmony_ci    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, u->sink_proplist);
213053a5a1b3Sopenharmony_ci
213153a5a1b3Sopenharmony_ci    u->sink = pa_sink_new(u->module->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY);
213253a5a1b3Sopenharmony_ci
213353a5a1b3Sopenharmony_ci    if (!u->sink) {
213453a5a1b3Sopenharmony_ci        pa_log("Failed to create sink.");
213553a5a1b3Sopenharmony_ci        goto finish;
213653a5a1b3Sopenharmony_ci    }
213753a5a1b3Sopenharmony_ci
213853a5a1b3Sopenharmony_ci    u->sink->parent.process_msg = sink_process_msg;
213953a5a1b3Sopenharmony_ci    u->sink->userdata = u;
214053a5a1b3Sopenharmony_ci    u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
214153a5a1b3Sopenharmony_ci    pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
214253a5a1b3Sopenharmony_ci    pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
214353a5a1b3Sopenharmony_ci
214453a5a1b3Sopenharmony_ci    u->sink->refresh_volume = u->sink->refresh_muted = false;
214553a5a1b3Sopenharmony_ci
214653a5a1b3Sopenharmony_ci/*     pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); */
214753a5a1b3Sopenharmony_ci
214853a5a1b3Sopenharmony_ci    pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
214953a5a1b3Sopenharmony_ci    pa_sink_set_rtpoll(u->sink, u->rtpoll);
215053a5a1b3Sopenharmony_ci    pa_sink_set_fixed_latency(u->sink, u->latency * PA_USEC_PER_MSEC);
215153a5a1b3Sopenharmony_ci
215253a5a1b3Sopenharmony_ci    pa_sink_put(u->sink);
215353a5a1b3Sopenharmony_ci
215453a5a1b3Sopenharmony_cifinish:
215553a5a1b3Sopenharmony_ci    pa_sink_new_data_done(&data);
215653a5a1b3Sopenharmony_ci    pa_xfree(data_name);
215753a5a1b3Sopenharmony_ci}
215853a5a1b3Sopenharmony_ci#else
215953a5a1b3Sopenharmony_cistatic void create_source(struct userdata *u) {
216053a5a1b3Sopenharmony_ci    pa_source_new_data data;
216153a5a1b3Sopenharmony_ci    char *data_name = NULL;
216253a5a1b3Sopenharmony_ci
216353a5a1b3Sopenharmony_ci    if (!(data_name = pa_xstrdup(u->configured_source_name)))
216453a5a1b3Sopenharmony_ci        data_name = pa_sprintf_malloc("tunnel-source.%s", u->server_name);
216553a5a1b3Sopenharmony_ci
216653a5a1b3Sopenharmony_ci    pa_source_new_data_init(&data);
216753a5a1b3Sopenharmony_ci    data.driver = __FILE__;
216853a5a1b3Sopenharmony_ci    data.module = u->module;
216953a5a1b3Sopenharmony_ci    data.namereg_fail = false;
217053a5a1b3Sopenharmony_ci    pa_source_new_data_set_name(&data, data_name);
217153a5a1b3Sopenharmony_ci    pa_source_new_data_set_sample_spec(&data, &u->sample_spec);
217253a5a1b3Sopenharmony_ci    pa_source_new_data_set_channel_map(&data, &u->channel_map);
217353a5a1b3Sopenharmony_ci    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name);
217453a5a1b3Sopenharmony_ci    pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
217553a5a1b3Sopenharmony_ci    if (u->source_name)
217653a5a1b3Sopenharmony_ci        pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
217753a5a1b3Sopenharmony_ci
217853a5a1b3Sopenharmony_ci    pa_proplist_update(data.proplist, PA_UPDATE_REPLACE, u->source_proplist);
217953a5a1b3Sopenharmony_ci
218053a5a1b3Sopenharmony_ci    u->source = pa_source_new(u->module->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
218153a5a1b3Sopenharmony_ci
218253a5a1b3Sopenharmony_ci    if (!u->source) {
218353a5a1b3Sopenharmony_ci        pa_log("Failed to create source.");
218453a5a1b3Sopenharmony_ci        goto finish;
218553a5a1b3Sopenharmony_ci    }
218653a5a1b3Sopenharmony_ci
218753a5a1b3Sopenharmony_ci    u->source->parent.process_msg = source_process_msg;
218853a5a1b3Sopenharmony_ci    u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb;
218953a5a1b3Sopenharmony_ci    u->source->userdata = u;
219053a5a1b3Sopenharmony_ci
219153a5a1b3Sopenharmony_ci/*     pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); */
219253a5a1b3Sopenharmony_ci
219353a5a1b3Sopenharmony_ci    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
219453a5a1b3Sopenharmony_ci    pa_source_set_rtpoll(u->source, u->rtpoll);
219553a5a1b3Sopenharmony_ci    pa_source_set_fixed_latency(u->source, u->latency * PA_USEC_PER_MSEC);
219653a5a1b3Sopenharmony_ci
219753a5a1b3Sopenharmony_ci    u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec));
219853a5a1b3Sopenharmony_ci
219953a5a1b3Sopenharmony_ci    pa_source_put(u->source);
220053a5a1b3Sopenharmony_ci
220153a5a1b3Sopenharmony_cifinish:
220253a5a1b3Sopenharmony_ci    pa_source_new_data_done(&data);
220353a5a1b3Sopenharmony_ci    pa_xfree(data_name);
220453a5a1b3Sopenharmony_ci}
220553a5a1b3Sopenharmony_ci#endif
220653a5a1b3Sopenharmony_ci
220753a5a1b3Sopenharmony_ci/* Runs in PA mainloop context */
220853a5a1b3Sopenharmony_cistatic int tunnel_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
220953a5a1b3Sopenharmony_ci    struct userdata *u = (struct userdata *) data;
221053a5a1b3Sopenharmony_ci
221153a5a1b3Sopenharmony_ci    pa_assert(u);
221253a5a1b3Sopenharmony_ci    pa_assert_ctl_context();
221353a5a1b3Sopenharmony_ci
221453a5a1b3Sopenharmony_ci    if (u->shutting_down)
221553a5a1b3Sopenharmony_ci        return 0;
221653a5a1b3Sopenharmony_ci
221753a5a1b3Sopenharmony_ci    switch (code) {
221853a5a1b3Sopenharmony_ci
221953a5a1b3Sopenharmony_ci        case TUNNEL_MESSAGE_MAYBE_RESTART:
222053a5a1b3Sopenharmony_ci            unload_module(u->module->userdata);
222153a5a1b3Sopenharmony_ci            break;
222253a5a1b3Sopenharmony_ci    }
222353a5a1b3Sopenharmony_ci
222453a5a1b3Sopenharmony_ci    return 0;
222553a5a1b3Sopenharmony_ci}
222653a5a1b3Sopenharmony_ci
222753a5a1b3Sopenharmony_cistatic int start_connect(struct userdata *u, char *server, bool automatic) {
222853a5a1b3Sopenharmony_ci    pa_strlist *server_list = NULL;
222953a5a1b3Sopenharmony_ci    int rc = 0;
223053a5a1b3Sopenharmony_ci
223153a5a1b3Sopenharmony_ci    if (server) {
223253a5a1b3Sopenharmony_ci        if (!(server_list = pa_strlist_parse(server))) {
223353a5a1b3Sopenharmony_ci            pa_log("Invalid server specified.");
223453a5a1b3Sopenharmony_ci            rc = -1;
223553a5a1b3Sopenharmony_ci            goto done;
223653a5a1b3Sopenharmony_ci        }
223753a5a1b3Sopenharmony_ci    } else {
223853a5a1b3Sopenharmony_ci        char *ufn;
223953a5a1b3Sopenharmony_ci
224053a5a1b3Sopenharmony_ci        if (!automatic) {
224153a5a1b3Sopenharmony_ci            pa_log("No server specified.");
224253a5a1b3Sopenharmony_ci            rc = -1;
224353a5a1b3Sopenharmony_ci            goto done;
224453a5a1b3Sopenharmony_ci        }
224553a5a1b3Sopenharmony_ci
224653a5a1b3Sopenharmony_ci        pa_log("No server address found. Attempting default local sockets.");
224753a5a1b3Sopenharmony_ci
224853a5a1b3Sopenharmony_ci        /* The system wide instance via PF_LOCAL */
224953a5a1b3Sopenharmony_ci        server_list = pa_strlist_prepend(server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
225053a5a1b3Sopenharmony_ci
225153a5a1b3Sopenharmony_ci        /* The user instance via PF_LOCAL */
225253a5a1b3Sopenharmony_ci        if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
225353a5a1b3Sopenharmony_ci            server_list = pa_strlist_prepend(server_list, ufn);
225453a5a1b3Sopenharmony_ci            pa_xfree(ufn);
225553a5a1b3Sopenharmony_ci        }
225653a5a1b3Sopenharmony_ci    }
225753a5a1b3Sopenharmony_ci
225853a5a1b3Sopenharmony_ci    for (;;) {
225953a5a1b3Sopenharmony_ci        server_list = pa_strlist_pop(server_list, &u->server_name);
226053a5a1b3Sopenharmony_ci
226153a5a1b3Sopenharmony_ci        if (!u->server_name) {
226253a5a1b3Sopenharmony_ci            if (server)
226353a5a1b3Sopenharmony_ci                pa_log("Failed to connect to server '%s'", server);
226453a5a1b3Sopenharmony_ci            else
226553a5a1b3Sopenharmony_ci                pa_log("Failed to connect");
226653a5a1b3Sopenharmony_ci            rc = -1;
226753a5a1b3Sopenharmony_ci            goto done;
226853a5a1b3Sopenharmony_ci        }
226953a5a1b3Sopenharmony_ci
227053a5a1b3Sopenharmony_ci        pa_log_debug("Trying to connect to %s...", u->server_name);
227153a5a1b3Sopenharmony_ci
227253a5a1b3Sopenharmony_ci        if (!(u->client = pa_socket_client_new_string(u->module->core->mainloop, true, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
227353a5a1b3Sopenharmony_ci            pa_xfree(u->server_name);
227453a5a1b3Sopenharmony_ci            u->server_name = NULL;
227553a5a1b3Sopenharmony_ci            continue;
227653a5a1b3Sopenharmony_ci        }
227753a5a1b3Sopenharmony_ci
227853a5a1b3Sopenharmony_ci        break;
227953a5a1b3Sopenharmony_ci    }
228053a5a1b3Sopenharmony_ci
228153a5a1b3Sopenharmony_ci    if (u->client)
228253a5a1b3Sopenharmony_ci        pa_socket_client_set_callback(u->client, on_connection, u);
228353a5a1b3Sopenharmony_ci
228453a5a1b3Sopenharmony_cidone:
228553a5a1b3Sopenharmony_ci    pa_strlist_free(server_list);
228653a5a1b3Sopenharmony_ci
228753a5a1b3Sopenharmony_ci    return rc;
228853a5a1b3Sopenharmony_ci}
228953a5a1b3Sopenharmony_ci
229053a5a1b3Sopenharmony_cistatic int do_init(pa_module *m) {
229153a5a1b3Sopenharmony_ci    pa_modargs *ma = NULL;
229253a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
229353a5a1b3Sopenharmony_ci    struct module_restart_data *rd;
229453a5a1b3Sopenharmony_ci    char *server = NULL;
229553a5a1b3Sopenharmony_ci    uint32_t latency_msec;
229653a5a1b3Sopenharmony_ci    bool automatic;
229753a5a1b3Sopenharmony_ci#ifdef HAVE_X11
229853a5a1b3Sopenharmony_ci    xcb_connection_t *xcb = NULL;
229953a5a1b3Sopenharmony_ci#endif
230053a5a1b3Sopenharmony_ci    const char *cookie_path;
230153a5a1b3Sopenharmony_ci    uint32_t reconnect_interval_ms = 0;
230253a5a1b3Sopenharmony_ci
230353a5a1b3Sopenharmony_ci    pa_assert(m);
230453a5a1b3Sopenharmony_ci    pa_assert(m->userdata);
230553a5a1b3Sopenharmony_ci
230653a5a1b3Sopenharmony_ci    rd = m->userdata;
230753a5a1b3Sopenharmony_ci
230853a5a1b3Sopenharmony_ci    if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
230953a5a1b3Sopenharmony_ci        pa_log("Failed to parse module arguments");
231053a5a1b3Sopenharmony_ci        goto fail;
231153a5a1b3Sopenharmony_ci    }
231253a5a1b3Sopenharmony_ci
231353a5a1b3Sopenharmony_ci    rd->userdata = u = pa_xnew0(struct userdata, 1);
231453a5a1b3Sopenharmony_ci    u->core = m->core;
231553a5a1b3Sopenharmony_ci    u->module = m;
231653a5a1b3Sopenharmony_ci    u->client = NULL;
231753a5a1b3Sopenharmony_ci    u->pdispatch = NULL;
231853a5a1b3Sopenharmony_ci    u->pstream = NULL;
231953a5a1b3Sopenharmony_ci    u->server_name = NULL;
232053a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
232153a5a1b3Sopenharmony_ci    u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
232253a5a1b3Sopenharmony_ci    u->configured_sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL));
232353a5a1b3Sopenharmony_ci    u->sink = NULL;
232453a5a1b3Sopenharmony_ci    u->requested_bytes = 0;
232553a5a1b3Sopenharmony_ci#else
232653a5a1b3Sopenharmony_ci    u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
232753a5a1b3Sopenharmony_ci    u->configured_source_name = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL));
232853a5a1b3Sopenharmony_ci    u->source = NULL;
232953a5a1b3Sopenharmony_ci#endif
233053a5a1b3Sopenharmony_ci#ifndef USE_SMOOTHER_2
233153a5a1b3Sopenharmony_ci    u->smoother = pa_smoother_new(
233253a5a1b3Sopenharmony_ci            PA_USEC_PER_SEC,
233353a5a1b3Sopenharmony_ci            PA_USEC_PER_SEC*2,
233453a5a1b3Sopenharmony_ci            true,
233553a5a1b3Sopenharmony_ci            true,
233653a5a1b3Sopenharmony_ci            10,
233753a5a1b3Sopenharmony_ci            pa_rtclock_now(),
233853a5a1b3Sopenharmony_ci            false);
233953a5a1b3Sopenharmony_ci#endif
234053a5a1b3Sopenharmony_ci    u->ctag = 1;
234153a5a1b3Sopenharmony_ci    u->device_index = u->channel = PA_INVALID_INDEX;
234253a5a1b3Sopenharmony_ci    u->time_event = NULL;
234353a5a1b3Sopenharmony_ci    u->ignore_latency_before = 0;
234453a5a1b3Sopenharmony_ci    u->transport_usec = u->thread_transport_usec = 0;
234553a5a1b3Sopenharmony_ci    u->remote_suspended = u->remote_corked = false;
234653a5a1b3Sopenharmony_ci    u->counter = 0;
234753a5a1b3Sopenharmony_ci    u->receive_snapshot = 0;
234853a5a1b3Sopenharmony_ci    u->receive_counter = 0;
234953a5a1b3Sopenharmony_ci
235053a5a1b3Sopenharmony_ci    u->msg = pa_msgobject_new(tunnel_msg);
235153a5a1b3Sopenharmony_ci    u->msg->parent.process_msg = tunnel_process_msg;
235253a5a1b3Sopenharmony_ci
235353a5a1b3Sopenharmony_ci    u->rtpoll = pa_rtpoll_new();
235453a5a1b3Sopenharmony_ci
235553a5a1b3Sopenharmony_ci    if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
235653a5a1b3Sopenharmony_ci        pa_log("pa_thread_mq_init() failed.");
235753a5a1b3Sopenharmony_ci        goto fail;
235853a5a1b3Sopenharmony_ci    }
235953a5a1b3Sopenharmony_ci
236053a5a1b3Sopenharmony_ci    automatic = false;
236153a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_boolean(ma, "auto", &automatic) < 0) {
236253a5a1b3Sopenharmony_ci        pa_log("Failed to parse argument \"auto\".");
236353a5a1b3Sopenharmony_ci        goto fail;
236453a5a1b3Sopenharmony_ci    }
236553a5a1b3Sopenharmony_ci
236653a5a1b3Sopenharmony_ci    /* Allow latencies between 5ms and 500ms */
236753a5a1b3Sopenharmony_ci    latency_msec = DEFAULT_LATENCY_MSEC;
236853a5a1b3Sopenharmony_ci    if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 5 || latency_msec > 500) {
236953a5a1b3Sopenharmony_ci        pa_log("Invalid latency specification");
237053a5a1b3Sopenharmony_ci        goto fail;
237153a5a1b3Sopenharmony_ci    }
237253a5a1b3Sopenharmony_ci
237353a5a1b3Sopenharmony_ci    u->latency = latency_msec;
237453a5a1b3Sopenharmony_ci
237553a5a1b3Sopenharmony_ci    cookie_path = pa_modargs_get_value(ma, "cookie", NULL);
237653a5a1b3Sopenharmony_ci    server = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL));
237753a5a1b3Sopenharmony_ci
237853a5a1b3Sopenharmony_ci    if (automatic) {
237953a5a1b3Sopenharmony_ci#ifdef HAVE_X11
238053a5a1b3Sopenharmony_ci        /* Need an X11 connection to get root properties */
238153a5a1b3Sopenharmony_ci        if (getenv("DISPLAY") != NULL) {
238253a5a1b3Sopenharmony_ci            if (!(xcb = xcb_connect(getenv("DISPLAY"), NULL)))
238353a5a1b3Sopenharmony_ci                pa_log("xcb_connect() failed");
238453a5a1b3Sopenharmony_ci            else {
238553a5a1b3Sopenharmony_ci                if (xcb_connection_has_error(xcb)) {
238653a5a1b3Sopenharmony_ci                    pa_log("xcb_connection_has_error() returned true");
238753a5a1b3Sopenharmony_ci                    xcb_disconnect(xcb);
238853a5a1b3Sopenharmony_ci                    xcb = NULL;
238953a5a1b3Sopenharmony_ci                }
239053a5a1b3Sopenharmony_ci            }
239153a5a1b3Sopenharmony_ci        }
239253a5a1b3Sopenharmony_ci#endif
239353a5a1b3Sopenharmony_ci
239453a5a1b3Sopenharmony_ci        /* Figure out the cookie the same way a normal client would */
239553a5a1b3Sopenharmony_ci        if (!cookie_path)
239653a5a1b3Sopenharmony_ci            cookie_path = getenv(ENV_COOKIE_FILE);
239753a5a1b3Sopenharmony_ci
239853a5a1b3Sopenharmony_ci#ifdef HAVE_X11
239953a5a1b3Sopenharmony_ci        if (!cookie_path && xcb) {
240053a5a1b3Sopenharmony_ci            char t[1024];
240153a5a1b3Sopenharmony_ci            if (pa_x11_get_prop(xcb, 0, "PULSE_COOKIE", t, sizeof(t))) {
240253a5a1b3Sopenharmony_ci                uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
240353a5a1b3Sopenharmony_ci
240453a5a1b3Sopenharmony_ci                if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie))
240553a5a1b3Sopenharmony_ci                    pa_log("Failed to parse cookie data");
240653a5a1b3Sopenharmony_ci                else {
240753a5a1b3Sopenharmony_ci                    if (!(u->auth_cookie = pa_auth_cookie_create(u->core, cookie, sizeof(cookie))))
240853a5a1b3Sopenharmony_ci                        goto fail;
240953a5a1b3Sopenharmony_ci                }
241053a5a1b3Sopenharmony_ci            }
241153a5a1b3Sopenharmony_ci        }
241253a5a1b3Sopenharmony_ci#endif
241353a5a1b3Sopenharmony_ci
241453a5a1b3Sopenharmony_ci        /* Same thing for the server name */
241553a5a1b3Sopenharmony_ci        if (!server)
241653a5a1b3Sopenharmony_ci            server = pa_xstrdup(getenv(ENV_DEFAULT_SERVER));
241753a5a1b3Sopenharmony_ci
241853a5a1b3Sopenharmony_ci#ifdef HAVE_X11
241953a5a1b3Sopenharmony_ci        if (!server && xcb) {
242053a5a1b3Sopenharmony_ci            char t[1024];
242153a5a1b3Sopenharmony_ci            if (pa_x11_get_prop(xcb, 0, "PULSE_SERVER", t, sizeof(t)))
242253a5a1b3Sopenharmony_ci                server = pa_xstrdup(t);
242353a5a1b3Sopenharmony_ci        }
242453a5a1b3Sopenharmony_ci#endif
242553a5a1b3Sopenharmony_ci
242653a5a1b3Sopenharmony_ci        /* Also determine the default sink/source on the other server */
242753a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
242853a5a1b3Sopenharmony_ci        if (!u->sink_name)
242953a5a1b3Sopenharmony_ci            u->sink_name = pa_xstrdup(getenv(ENV_DEFAULT_SINK));
243053a5a1b3Sopenharmony_ci
243153a5a1b3Sopenharmony_ci#ifdef HAVE_X11
243253a5a1b3Sopenharmony_ci        if (!u->sink_name && xcb) {
243353a5a1b3Sopenharmony_ci            char t[1024];
243453a5a1b3Sopenharmony_ci            if (pa_x11_get_prop(xcb, 0, "PULSE_SINK", t, sizeof(t)))
243553a5a1b3Sopenharmony_ci                u->sink_name = pa_xstrdup(t);
243653a5a1b3Sopenharmony_ci        }
243753a5a1b3Sopenharmony_ci#endif
243853a5a1b3Sopenharmony_ci#else
243953a5a1b3Sopenharmony_ci        if (!u->source_name)
244053a5a1b3Sopenharmony_ci            u->source_name = pa_xstrdup(getenv(ENV_DEFAULT_SOURCE));
244153a5a1b3Sopenharmony_ci
244253a5a1b3Sopenharmony_ci#ifdef HAVE_X11
244353a5a1b3Sopenharmony_ci        if (!u->source_name && xcb) {
244453a5a1b3Sopenharmony_ci            char t[1024];
244553a5a1b3Sopenharmony_ci            if (pa_x11_get_prop(xcb, 0, "PULSE_SOURCE", t, sizeof(t)))
244653a5a1b3Sopenharmony_ci                u->source_name = pa_xstrdup(t);
244753a5a1b3Sopenharmony_ci        }
244853a5a1b3Sopenharmony_ci#endif
244953a5a1b3Sopenharmony_ci#endif
245053a5a1b3Sopenharmony_ci    }
245153a5a1b3Sopenharmony_ci
245253a5a1b3Sopenharmony_ci    if (!cookie_path && !u->auth_cookie)
245353a5a1b3Sopenharmony_ci        cookie_path = PA_NATIVE_COOKIE_FILE;
245453a5a1b3Sopenharmony_ci
245553a5a1b3Sopenharmony_ci    if (cookie_path) {
245653a5a1b3Sopenharmony_ci        if (!(u->auth_cookie = pa_auth_cookie_get(u->core, cookie_path, true, PA_NATIVE_COOKIE_LENGTH)))
245753a5a1b3Sopenharmony_ci            goto fail;
245853a5a1b3Sopenharmony_ci    }
245953a5a1b3Sopenharmony_ci
246053a5a1b3Sopenharmony_ci    u->sample_spec = m->core->default_sample_spec;
246153a5a1b3Sopenharmony_ci    u->channel_map = m->core->default_channel_map;
246253a5a1b3Sopenharmony_ci    if (pa_modargs_get_sample_spec_and_channel_map(ma, &u->sample_spec, &u->channel_map, PA_CHANNEL_MAP_DEFAULT) < 0) {
246353a5a1b3Sopenharmony_ci        pa_log("Invalid sample format specification");
246453a5a1b3Sopenharmony_ci        goto fail;
246553a5a1b3Sopenharmony_ci    }
246653a5a1b3Sopenharmony_ci
246753a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
246853a5a1b3Sopenharmony_ci    /* Smoother window must be larger than time between updates. */
246953a5a1b3Sopenharmony_ci    u->smoother = pa_smoother_2_new(LATENCY_INTERVAL + 5*PA_USEC_PER_SEC, pa_rtclock_now(), pa_frame_size(&u->sample_spec), u->sample_spec.rate);
247053a5a1b3Sopenharmony_ci#endif
247153a5a1b3Sopenharmony_ci
247253a5a1b3Sopenharmony_ci    pa_modargs_get_value_u32(ma, "reconnect_interval_ms", &reconnect_interval_ms);
247353a5a1b3Sopenharmony_ci    u->reconnect_interval_us = reconnect_interval_ms * PA_USEC_PER_MSEC;
247453a5a1b3Sopenharmony_ci
247553a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
247653a5a1b3Sopenharmony_ci
247753a5a1b3Sopenharmony_ci    u->sink_proplist = pa_proplist_new();
247853a5a1b3Sopenharmony_ci    if (pa_modargs_get_proplist(ma, "sink_properties", u->sink_proplist, PA_UPDATE_REPLACE) < 0) {
247953a5a1b3Sopenharmony_ci        pa_log("Invalid properties");
248053a5a1b3Sopenharmony_ci        goto fail;
248153a5a1b3Sopenharmony_ci    }
248253a5a1b3Sopenharmony_ci
248353a5a1b3Sopenharmony_ci#else
248453a5a1b3Sopenharmony_ci
248553a5a1b3Sopenharmony_ci    u->source_proplist = pa_proplist_new();
248653a5a1b3Sopenharmony_ci    if (pa_modargs_get_proplist(ma, "source_properties", u->source_proplist, PA_UPDATE_REPLACE) < 0) {
248753a5a1b3Sopenharmony_ci        pa_log("Invalid properties");
248853a5a1b3Sopenharmony_ci        goto fail;
248953a5a1b3Sopenharmony_ci    }
249053a5a1b3Sopenharmony_ci
249153a5a1b3Sopenharmony_ci#endif
249253a5a1b3Sopenharmony_ci
249353a5a1b3Sopenharmony_ci    u->time_event = NULL;
249453a5a1b3Sopenharmony_ci
249553a5a1b3Sopenharmony_ci    u->maxlength = (uint32_t) -1;
249653a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
249753a5a1b3Sopenharmony_ci    u->tlength = u->minreq = u->prebuf = (uint32_t) -1;
249853a5a1b3Sopenharmony_ci#else
249953a5a1b3Sopenharmony_ci    u->fragsize = (uint32_t) -1;
250053a5a1b3Sopenharmony_ci#endif
250153a5a1b3Sopenharmony_ci
250253a5a1b3Sopenharmony_ci    if (start_connect(u, server, automatic) < 0) {
250353a5a1b3Sopenharmony_ci        goto fail;
250453a5a1b3Sopenharmony_ci    }
250553a5a1b3Sopenharmony_ci
250653a5a1b3Sopenharmony_ci    if (!(u->thread = pa_thread_new("module-tunnel", thread_func, u))) {
250753a5a1b3Sopenharmony_ci        pa_log("Failed to create thread.");
250853a5a1b3Sopenharmony_ci        goto fail;
250953a5a1b3Sopenharmony_ci    }
251053a5a1b3Sopenharmony_ci
251153a5a1b3Sopenharmony_ci    if (server)
251253a5a1b3Sopenharmony_ci        pa_xfree(server);
251353a5a1b3Sopenharmony_ci
251453a5a1b3Sopenharmony_ci#ifdef HAVE_X11
251553a5a1b3Sopenharmony_ci    if (xcb)
251653a5a1b3Sopenharmony_ci        xcb_disconnect(xcb);
251753a5a1b3Sopenharmony_ci#endif
251853a5a1b3Sopenharmony_ci
251953a5a1b3Sopenharmony_ci    /* If the module is restarting and do_init() finishes successfully, the
252053a5a1b3Sopenharmony_ci     * restart data is no longer needed. If do_init() fails, don't touch the
252153a5a1b3Sopenharmony_ci     * restart data, because following restart attempts will continue to use
252253a5a1b3Sopenharmony_ci     * the same data. If restart_data is NULL, that means no restart is
252353a5a1b3Sopenharmony_ci     * currently pending. */
252453a5a1b3Sopenharmony_ci    if (rd->restart_data) {
252553a5a1b3Sopenharmony_ci        pa_restart_free(rd->restart_data);
252653a5a1b3Sopenharmony_ci        rd->restart_data = NULL;
252753a5a1b3Sopenharmony_ci    }
252853a5a1b3Sopenharmony_ci
252953a5a1b3Sopenharmony_ci    pa_modargs_free(ma);
253053a5a1b3Sopenharmony_ci
253153a5a1b3Sopenharmony_ci    return 0;
253253a5a1b3Sopenharmony_ci
253353a5a1b3Sopenharmony_cifail:
253453a5a1b3Sopenharmony_ci    if (server)
253553a5a1b3Sopenharmony_ci        pa_xfree(server);
253653a5a1b3Sopenharmony_ci
253753a5a1b3Sopenharmony_ci#ifdef HAVE_X11
253853a5a1b3Sopenharmony_ci    if (xcb)
253953a5a1b3Sopenharmony_ci        xcb_disconnect(xcb);
254053a5a1b3Sopenharmony_ci#endif
254153a5a1b3Sopenharmony_ci
254253a5a1b3Sopenharmony_ci    if (ma)
254353a5a1b3Sopenharmony_ci        pa_modargs_free(ma);
254453a5a1b3Sopenharmony_ci
254553a5a1b3Sopenharmony_ci    return -1;
254653a5a1b3Sopenharmony_ci}
254753a5a1b3Sopenharmony_ci
254853a5a1b3Sopenharmony_cistatic void do_done(pa_module *m) {
254953a5a1b3Sopenharmony_ci    struct userdata *u = NULL;
255053a5a1b3Sopenharmony_ci    struct module_restart_data *rd;
255153a5a1b3Sopenharmony_ci
255253a5a1b3Sopenharmony_ci    pa_assert(m);
255353a5a1b3Sopenharmony_ci
255453a5a1b3Sopenharmony_ci    if (!(rd = m->userdata))
255553a5a1b3Sopenharmony_ci        return;
255653a5a1b3Sopenharmony_ci    if (!(u = rd->userdata))
255753a5a1b3Sopenharmony_ci        return;
255853a5a1b3Sopenharmony_ci
255953a5a1b3Sopenharmony_ci    u->shutting_down = true;
256053a5a1b3Sopenharmony_ci
256153a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
256253a5a1b3Sopenharmony_ci    if (u->sink)
256353a5a1b3Sopenharmony_ci        pa_sink_unlink(u->sink);
256453a5a1b3Sopenharmony_ci#else
256553a5a1b3Sopenharmony_ci    if (u->source)
256653a5a1b3Sopenharmony_ci        pa_source_unlink(u->source);
256753a5a1b3Sopenharmony_ci#endif
256853a5a1b3Sopenharmony_ci
256953a5a1b3Sopenharmony_ci    if (u->thread) {
257053a5a1b3Sopenharmony_ci        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
257153a5a1b3Sopenharmony_ci        pa_thread_free(u->thread);
257253a5a1b3Sopenharmony_ci    }
257353a5a1b3Sopenharmony_ci
257453a5a1b3Sopenharmony_ci    pa_thread_mq_done(&u->thread_mq);
257553a5a1b3Sopenharmony_ci
257653a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
257753a5a1b3Sopenharmony_ci    if (u->sink)
257853a5a1b3Sopenharmony_ci        pa_sink_unref(u->sink);
257953a5a1b3Sopenharmony_ci#else
258053a5a1b3Sopenharmony_ci    if (u->source)
258153a5a1b3Sopenharmony_ci        pa_source_unref(u->source);
258253a5a1b3Sopenharmony_ci#endif
258353a5a1b3Sopenharmony_ci
258453a5a1b3Sopenharmony_ci    if (u->rtpoll)
258553a5a1b3Sopenharmony_ci        pa_rtpoll_free(u->rtpoll);
258653a5a1b3Sopenharmony_ci
258753a5a1b3Sopenharmony_ci    if (u->pstream) {
258853a5a1b3Sopenharmony_ci        pa_pstream_unlink(u->pstream);
258953a5a1b3Sopenharmony_ci        pa_pstream_unref(u->pstream);
259053a5a1b3Sopenharmony_ci    }
259153a5a1b3Sopenharmony_ci
259253a5a1b3Sopenharmony_ci    if (u->pdispatch)
259353a5a1b3Sopenharmony_ci        pa_pdispatch_unref(u->pdispatch);
259453a5a1b3Sopenharmony_ci
259553a5a1b3Sopenharmony_ci    if (u->client)
259653a5a1b3Sopenharmony_ci        pa_socket_client_unref(u->client);
259753a5a1b3Sopenharmony_ci
259853a5a1b3Sopenharmony_ci    if (u->auth_cookie)
259953a5a1b3Sopenharmony_ci        pa_auth_cookie_unref(u->auth_cookie);
260053a5a1b3Sopenharmony_ci
260153a5a1b3Sopenharmony_ci    if (u->smoother)
260253a5a1b3Sopenharmony_ci#ifdef USE_SMOOTHER_2
260353a5a1b3Sopenharmony_ci        pa_smoother_2_free(u->smoother);
260453a5a1b3Sopenharmony_ci#else
260553a5a1b3Sopenharmony_ci        pa_smoother_free(u->smoother);
260653a5a1b3Sopenharmony_ci#endif
260753a5a1b3Sopenharmony_ci
260853a5a1b3Sopenharmony_ci    if (u->time_event)
260953a5a1b3Sopenharmony_ci        u->core->mainloop->time_free(u->time_event);
261053a5a1b3Sopenharmony_ci
261153a5a1b3Sopenharmony_ci#ifndef TUNNEL_SINK
261253a5a1b3Sopenharmony_ci    if (u->mcalign)
261353a5a1b3Sopenharmony_ci        pa_mcalign_free(u->mcalign);
261453a5a1b3Sopenharmony_ci#endif
261553a5a1b3Sopenharmony_ci
261653a5a1b3Sopenharmony_ci#ifdef TUNNEL_SINK
261753a5a1b3Sopenharmony_ci    pa_xfree(u->sink_name);
261853a5a1b3Sopenharmony_ci    pa_xfree(u->configured_sink_name);
261953a5a1b3Sopenharmony_ci    pa_proplist_free(u->sink_proplist);
262053a5a1b3Sopenharmony_ci#else
262153a5a1b3Sopenharmony_ci    pa_xfree(u->source_name);
262253a5a1b3Sopenharmony_ci    pa_xfree(u->configured_source_name);
262353a5a1b3Sopenharmony_ci    pa_proplist_free(u->source_proplist);
262453a5a1b3Sopenharmony_ci#endif
262553a5a1b3Sopenharmony_ci    pa_xfree(u->server_name);
262653a5a1b3Sopenharmony_ci
262753a5a1b3Sopenharmony_ci    pa_xfree(u->device_description);
262853a5a1b3Sopenharmony_ci    pa_xfree(u->server_fqdn);
262953a5a1b3Sopenharmony_ci    pa_xfree(u->user_name);
263053a5a1b3Sopenharmony_ci
263153a5a1b3Sopenharmony_ci    pa_xfree(u->msg);
263253a5a1b3Sopenharmony_ci
263353a5a1b3Sopenharmony_ci    pa_xfree(u);
263453a5a1b3Sopenharmony_ci
263553a5a1b3Sopenharmony_ci    rd->userdata = NULL;
263653a5a1b3Sopenharmony_ci}
263753a5a1b3Sopenharmony_ci
263853a5a1b3Sopenharmony_ciint pa__init(pa_module *m) {
263953a5a1b3Sopenharmony_ci    int ret;
264053a5a1b3Sopenharmony_ci
264153a5a1b3Sopenharmony_ci    pa_assert(m);
264253a5a1b3Sopenharmony_ci
264353a5a1b3Sopenharmony_ci    m->userdata = pa_xnew0(struct module_restart_data, 1);
264453a5a1b3Sopenharmony_ci
264553a5a1b3Sopenharmony_ci    ret = do_init(m);
264653a5a1b3Sopenharmony_ci
264753a5a1b3Sopenharmony_ci    if (ret < 0)
264853a5a1b3Sopenharmony_ci        pa__done(m);
264953a5a1b3Sopenharmony_ci
265053a5a1b3Sopenharmony_ci    return ret;
265153a5a1b3Sopenharmony_ci}
265253a5a1b3Sopenharmony_ci
265353a5a1b3Sopenharmony_civoid pa__done(pa_module *m) {
265453a5a1b3Sopenharmony_ci    pa_assert(m);
265553a5a1b3Sopenharmony_ci
265653a5a1b3Sopenharmony_ci    do_done(m);
265753a5a1b3Sopenharmony_ci
265853a5a1b3Sopenharmony_ci    if (m->userdata) {
265953a5a1b3Sopenharmony_ci        struct module_restart_data *rd = m->userdata;
266053a5a1b3Sopenharmony_ci
266153a5a1b3Sopenharmony_ci        if (rd->restart_data)
266253a5a1b3Sopenharmony_ci            pa_restart_free(rd->restart_data);
266353a5a1b3Sopenharmony_ci
266453a5a1b3Sopenharmony_ci        pa_xfree(m->userdata);
266553a5a1b3Sopenharmony_ci    }
266653a5a1b3Sopenharmony_ci}
2667