2004-09-28 22:47:48 +00:00
|
|
|
/***
|
2006-06-19 21:53:48 +00:00
|
|
|
This file is part of PulseAudio.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-02-13 15:35:19 +00:00
|
|
|
Copyright 2004-2006 Lennart Poettering
|
|
|
|
|
Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
|
|
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is free software; you can redistribute it and/or modify
|
2004-11-14 14:58:54 +00:00
|
|
|
it under the terms of the GNU Lesser General Public License as published
|
2009-03-03 20:23:02 +00:00
|
|
|
by the Free Software Foundation; either version 2.1 of the License,
|
2004-09-28 22:47:48 +00:00
|
|
|
or (at your option) any later version.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
PulseAudio is distributed in the hope that it will be useful, but
|
2004-09-28 22:47:48 +00:00
|
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
General Public License for more details.
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-11-14 14:58:54 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
2014-11-26 14:14:51 +01:00
|
|
|
along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
|
2004-09-28 22:47:48 +00:00
|
|
|
***/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
#include <xcb/xcb.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
2009-04-04 23:19:53 +03:00
|
|
|
#include <pulse/rtclock.h>
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulse/timeval.h>
|
|
|
|
|
#include <pulse/util.h>
|
|
|
|
|
#include <pulse/version.h>
|
|
|
|
|
#include <pulse/xmalloc.h>
|
2006-03-04 21:30:29 +00:00
|
|
|
|
2006-06-19 21:53:48 +00:00
|
|
|
#include <pulsecore/module.h>
|
|
|
|
|
#include <pulsecore/core-util.h>
|
|
|
|
|
#include <pulsecore/modargs.h>
|
|
|
|
|
#include <pulsecore/log.h>
|
|
|
|
|
#include <pulsecore/core-subscribe.h>
|
|
|
|
|
#include <pulsecore/pdispatch.h>
|
|
|
|
|
#include <pulsecore/pstream.h>
|
|
|
|
|
#include <pulsecore/pstream-util.h>
|
|
|
|
|
#include <pulsecore/socket-client.h>
|
2007-10-29 16:54:16 +00:00
|
|
|
#include <pulsecore/time-smoother.h>
|
|
|
|
|
#include <pulsecore/thread.h>
|
|
|
|
|
#include <pulsecore/thread-mq.h>
|
2009-04-04 23:19:53 +03:00
|
|
|
#include <pulsecore/core-rtclock.h>
|
2007-10-29 16:54:16 +00:00
|
|
|
#include <pulsecore/core-error.h>
|
2008-06-17 18:29:15 +00:00
|
|
|
#include <pulsecore/proplist-util.h>
|
2008-08-03 16:44:38 +02:00
|
|
|
#include <pulsecore/auth-cookie.h>
|
2009-07-31 00:50:19 +02:00
|
|
|
#include <pulsecore/mcalign.h>
|
2013-07-11 13:57:41 +02:00
|
|
|
#include <pulsecore/strlist.h>
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
#include <pulsecore/x11prop.h>
|
|
|
|
|
#endif
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
#define ENV_DEFAULT_SINK "PULSE_SINK"
|
|
|
|
|
#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
|
|
|
|
|
#define ENV_DEFAULT_SERVER "PULSE_SERVER"
|
|
|
|
|
#define ENV_COOKIE_FILE "PULSE_COOKIE"
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2007-11-09 18:25:40 +00:00
|
|
|
PA_MODULE_DESCRIPTION("Tunnel module for sinks");
|
2006-04-26 15:40:14 +00:00
|
|
|
PA_MODULE_USAGE(
|
2009-05-28 02:39:22 +02:00
|
|
|
"sink_name=<name for the local sink> "
|
|
|
|
|
"sink_properties=<properties for the local sink> "
|
2013-07-11 13:57:41 +02:00
|
|
|
"auto=<determine server/sink/cookie automatically> "
|
2006-04-26 15:40:14 +00:00
|
|
|
"server=<address> "
|
|
|
|
|
"sink=<remote sink name> "
|
|
|
|
|
"cookie=<filename> "
|
|
|
|
|
"format=<sample format> "
|
|
|
|
|
"channels=<number of channels> "
|
|
|
|
|
"rate=<sample rate> "
|
2007-11-09 18:25:40 +00:00
|
|
|
"channel_map=<channel map>");
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
2007-11-09 18:25:40 +00:00
|
|
|
PA_MODULE_DESCRIPTION("Tunnel module for sources");
|
2006-04-26 15:40:14 +00:00
|
|
|
PA_MODULE_USAGE(
|
2009-05-28 02:39:22 +02:00
|
|
|
"source_name=<name for the local source> "
|
|
|
|
|
"source_properties=<properties for the local source> "
|
2013-07-11 13:57:41 +02:00
|
|
|
"auto=<determine server/source/cookie automatically> "
|
2006-04-26 15:40:14 +00:00
|
|
|
"server=<address> "
|
|
|
|
|
"source=<remote source name> "
|
|
|
|
|
"cookie=<filename> "
|
|
|
|
|
"format=<sample format> "
|
|
|
|
|
"channels=<number of channels> "
|
|
|
|
|
"rate=<sample rate> "
|
2007-11-09 18:25:40 +00:00
|
|
|
"channel_map=<channel map>");
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
|
|
|
|
|
2007-11-09 18:25:40 +00:00
|
|
|
PA_MODULE_AUTHOR("Lennart Poettering");
|
|
|
|
|
PA_MODULE_VERSION(PACKAGE_VERSION);
|
2013-06-27 19:28:09 +02:00
|
|
|
PA_MODULE_LOAD_ONCE(false);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
static const char* const valid_modargs[] = {
|
2013-07-11 13:57:41 +02:00
|
|
|
"auto",
|
2004-09-28 22:47:48 +00:00
|
|
|
"server",
|
|
|
|
|
"cookie",
|
|
|
|
|
"format",
|
|
|
|
|
"channels",
|
|
|
|
|
"rate",
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2004-09-28 22:47:48 +00:00
|
|
|
"sink_name",
|
2009-05-28 02:39:22 +02:00
|
|
|
"sink_properties",
|
2004-09-29 17:38:45 +00:00
|
|
|
"sink",
|
|
|
|
|
#else
|
|
|
|
|
"source_name",
|
2009-05-28 02:39:22 +02:00
|
|
|
"source_properties",
|
2004-09-29 17:38:45 +00:00
|
|
|
"source",
|
|
|
|
|
#endif
|
2006-04-26 15:40:14 +00:00
|
|
|
"channel_map",
|
2004-09-28 22:47:48 +00:00
|
|
|
NULL,
|
|
|
|
|
};
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#define DEFAULT_TIMEOUT 5
|
|
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
#define LATENCY_INTERVAL (10*PA_USEC_PER_SEC)
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
#define MIN_NETWORK_LATENCY_USEC (8*PA_USEC_PER_MSEC)
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
SINK_MESSAGE_REQUEST = PA_SINK_MESSAGE_MAX,
|
2008-06-17 18:29:15 +00:00
|
|
|
SINK_MESSAGE_REMOTE_SUSPEND,
|
|
|
|
|
SINK_MESSAGE_UPDATE_LATENCY,
|
2007-10-29 16:54:16 +00:00
|
|
|
SINK_MESSAGE_POST
|
|
|
|
|
};
|
2004-09-29 17:38:45 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#define DEFAULT_TLENGTH_MSEC 150
|
|
|
|
|
#define DEFAULT_MINREQ_MSEC 25
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
SOURCE_MESSAGE_POST = PA_SOURCE_MESSAGE_MAX,
|
|
|
|
|
SOURCE_MESSAGE_REMOTE_SUSPEND,
|
|
|
|
|
SOURCE_MESSAGE_UPDATE_LATENCY
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define DEFAULT_FRAGSIZE_MSEC 25
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2006-01-11 01:17:39 +00:00
|
|
|
static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2008-06-17 18:29:15 +00:00
|
|
|
static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-10-30 01:50:22 +00:00
|
|
|
static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2007-10-29 16:54:16 +00:00
|
|
|
static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2008-06-17 18:29:15 +00:00
|
|
|
static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
|
|
|
|
static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2008-01-04 14:57:31 +00:00
|
|
|
static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2009-03-30 19:13:07 +02:00
|
|
|
static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2009-03-31 21:35:34 +02:00
|
|
|
static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2006-02-20 22:41:02 +00:00
|
|
|
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2006-01-11 01:17:39 +00:00
|
|
|
[PA_COMMAND_REQUEST] = command_request,
|
2008-06-17 18:29:15 +00:00
|
|
|
[PA_COMMAND_STARTED] = command_started,
|
2007-01-04 13:43:45 +00:00
|
|
|
#endif
|
2007-10-30 01:50:22 +00:00
|
|
|
[PA_COMMAND_SUBSCRIBE_EVENT] = command_subscribe_event,
|
2008-06-17 18:29:15 +00:00
|
|
|
[PA_COMMAND_OVERFLOW] = command_overflow_or_underflow,
|
|
|
|
|
[PA_COMMAND_UNDERFLOW] = command_overflow_or_underflow,
|
2006-01-11 01:17:39 +00:00
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_KILLED] = command_stream_killed,
|
2006-03-05 20:18:04 +00:00
|
|
|
[PA_COMMAND_RECORD_STREAM_KILLED] = command_stream_killed,
|
2008-06-17 18:29:15 +00:00
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = command_suspended,
|
|
|
|
|
[PA_COMMAND_RECORD_STREAM_SUSPENDED] = command_suspended,
|
2008-01-04 14:57:31 +00:00
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_MOVED] = command_moved,
|
2009-03-30 19:13:07 +02:00
|
|
|
[PA_COMMAND_RECORD_STREAM_MOVED] = command_moved,
|
|
|
|
|
[PA_COMMAND_PLAYBACK_STREAM_EVENT] = command_stream_or_client_event,
|
|
|
|
|
[PA_COMMAND_RECORD_STREAM_EVENT] = command_stream_or_client_event,
|
2009-03-31 21:35:34 +02:00
|
|
|
[PA_COMMAND_CLIENT_EVENT] = command_stream_or_client_event,
|
|
|
|
|
[PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed,
|
|
|
|
|
[PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = command_stream_buffer_attr_changed
|
2004-09-28 22:47:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct userdata {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_core *core;
|
|
|
|
|
pa_module *module;
|
|
|
|
|
|
|
|
|
|
pa_thread_mq thread_mq;
|
|
|
|
|
pa_rtpoll *rtpoll;
|
|
|
|
|
pa_thread *thread;
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_socket_client *client;
|
|
|
|
|
pa_pstream *pstream;
|
|
|
|
|
pa_pdispatch *pdispatch;
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
char *server_name;
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
char *sink_name;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sink *sink;
|
2008-08-19 22:39:54 +02:00
|
|
|
size_t requested_bytes;
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
|
|
|
|
char *source_name;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_source *source;
|
2009-07-31 00:50:19 +02:00
|
|
|
pa_mcalign *mcalign;
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
pa_auth_cookie *auth_cookie;
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2006-03-04 21:30:29 +00:00
|
|
|
uint32_t version;
|
2004-09-28 22:47:48 +00:00
|
|
|
uint32_t ctag;
|
|
|
|
|
uint32_t device_index;
|
|
|
|
|
uint32_t channel;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
int64_t counter, counter_delta;
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
bool remote_corked:1;
|
|
|
|
|
bool remote_suspended:1;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_usec_t transport_usec; /* maintained in the main thread */
|
|
|
|
|
pa_usec_t thread_transport_usec; /* maintained in the IO thread */
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
uint32_t ignore_latency_before;
|
|
|
|
|
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_time_event *time_event;
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_smoother *smoother;
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
char *device_description;
|
|
|
|
|
char *server_fqdn;
|
|
|
|
|
char *user_name;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
uint32_t maxlength;
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2007-10-29 16:54:16 +00:00
|
|
|
uint32_t tlength;
|
|
|
|
|
uint32_t minreq;
|
|
|
|
|
uint32_t prebuf;
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
2007-10-29 16:54:16 +00:00
|
|
|
uint32_t fragsize;
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-10-29 16:54:16 +00:00
|
|
|
};
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
static void request_latency(struct userdata *u);
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_stream_or_client_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
pa_log_debug("Got stream or client event.");
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_stream_killed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-10-29 16:54:16 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log_warn("Stream killed");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_overflow_or_underflow(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2004-09-29 17:38:45 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
2004-09-29 17:38:45 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log_info("Server signalled buffer overrun/underrun.");
|
|
|
|
|
request_latency(u);
|
2007-10-29 16:54:16 +00:00
|
|
|
}
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_suspended(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2006-03-05 20:18:04 +00:00
|
|
|
struct userdata *u = userdata;
|
2008-06-17 18:29:15 +00:00
|
|
|
uint32_t channel;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool suspended;
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &suspended) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
2009-03-30 19:13:07 +02:00
|
|
|
|
|
|
|
|
pa_log("Invalid packet.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2008-06-17 18:29:15 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_log_debug("Server reports device suspend.");
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2014-10-23 15:00:29 +02:00
|
|
|
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
|
2008-06-17 18:29:15 +00:00
|
|
|
#else
|
2014-10-23 15:00:29 +02:00
|
|
|
pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
|
2008-06-17 18:29:15 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
request_latency(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_moved(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2008-01-04 14:57:31 +00:00
|
|
|
struct userdata *u = userdata;
|
2009-03-30 19:13:07 +02:00
|
|
|
uint32_t channel, di;
|
|
|
|
|
const char *dn;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool suspended;
|
2008-01-04 14:57:31 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &di) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &dn) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &suspended) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log_error("Invalid packet.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2009-03-30 19:13:07 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log_debug("Server reports a stream move.");
|
2009-03-30 19:13:07 +02:00
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2014-10-23 15:00:29 +02:00
|
|
|
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
|
2009-03-30 19:13:07 +02:00
|
|
|
#else
|
2014-10-23 15:00:29 +02:00
|
|
|
pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_REMOTE_SUSPEND, PA_UINT32_TO_PTR(suspended), 0, NULL);
|
2009-03-30 19:13:07 +02:00
|
|
|
#endif
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
request_latency(u);
|
2008-01-04 14:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-31 21:35:34 +02:00
|
|
|
static void command_stream_buffer_attr_changed(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
|
|
|
|
struct userdata *u = userdata;
|
2009-09-08 23:46:23 +02:00
|
|
|
uint32_t channel, maxlength, tlength = 0, fragsize, prebuf, minreq;
|
2009-03-31 21:35:34 +02:00
|
|
|
pa_usec_t usec;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &maxlength) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log_error("Invalid packet.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2009-03-31 21:35:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (command == PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &fragsize) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log_error("Invalid packet.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2009-03-31 21:35:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &tlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &prebuf) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &minreq) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &usec) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log_error("Invalid packet.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2009-03-31 21:35:34 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_log_debug("Server reports buffer attrs changed. tlength now at %lu, before %lu.", (unsigned long) tlength, (unsigned long) u->tlength);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
request_latency(u);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
|
|
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_started(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2011-03-02 12:41:26 +01:00
|
|
|
struct userdata *u = userdata;
|
2008-01-04 14:57:31 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log_debug("Server reports playback started.");
|
|
|
|
|
request_latency(u);
|
2008-01-04 14:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Called from IO thread context */
|
2013-06-27 19:28:09 +02:00
|
|
|
static void check_smoother_status(struct userdata *u, bool past) {
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_usec_t x;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_assert(u);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-04-04 22:56:38 +03:00
|
|
|
x = pa_rtclock_now();
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* Correct by the time the requested issued needs to travel to the
|
|
|
|
|
* other side. This is a valid thread-safe access, because the
|
|
|
|
|
* main thread is waiting for us */
|
|
|
|
|
|
|
|
|
|
if (past)
|
|
|
|
|
x -= u->thread_transport_usec;
|
|
|
|
|
else
|
|
|
|
|
x += u->thread_transport_usec;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
if (u->remote_suspended || u->remote_corked)
|
|
|
|
|
pa_smoother_pause(u->smoother, x);
|
2007-10-29 16:54:16 +00:00
|
|
|
else
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_smoother_resume(u->smoother, x, true);
|
2008-06-17 18:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* Called from IO thread context */
|
2013-06-27 19:28:09 +02:00
|
|
|
static void stream_cork_within_thread(struct userdata *u, bool cork) {
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
if (u->remote_corked == cork)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
u->remote_corked = cork;
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(u, false);
|
2009-03-30 19:13:07 +02:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2013-06-27 19:28:09 +02:00
|
|
|
static void stream_cork(struct userdata *u, bool cork) {
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct *t;
|
|
|
|
|
pa_assert(u);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2007-10-29 22:14:34 +00:00
|
|
|
if (!u->pstream)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_CORK_PLAYBACK_STREAM);
|
|
|
|
|
#else
|
|
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_CORK_RECORD_STREAM);
|
|
|
|
|
#endif
|
2004-09-28 23:49:54 +00:00
|
|
|
pa_tagstruct_putu32(t, u->ctag++);
|
|
|
|
|
pa_tagstruct_putu32(t, u->channel);
|
2014-10-23 15:00:29 +02:00
|
|
|
pa_tagstruct_put_boolean(t, cork);
|
2004-09-28 23:49:54 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
request_latency(u);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Called from IO thread context */
|
2013-06-27 19:28:09 +02:00
|
|
|
static void stream_suspend_within_thread(struct userdata *u, bool suspend) {
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
if (u->remote_suspended == suspend)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
u->remote_suspended = suspend;
|
2013-06-27 19:28:09 +02:00
|
|
|
check_smoother_status(u, true);
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from IO thread context */
|
2007-10-29 16:54:16 +00:00
|
|
|
static void send_data(struct userdata *u) {
|
|
|
|
|
pa_assert(u);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
while (u->requested_bytes > 0) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_memchunk memchunk;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_sink_render(u->sink, u->requested_bytes, &memchunk);
|
|
|
|
|
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_POST, NULL, 0, &memchunk, NULL);
|
|
|
|
|
pa_memblock_unref(memchunk.memblock);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
u->requested_bytes -= memchunk.length;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
u->counter += (int64_t) memchunk.length;
|
2007-10-29 16:54:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function is called from IO context -- except when it is not. */
|
|
|
|
|
static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
struct userdata *u = PA_SINK(o)->userdata;
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
case PA_SINK_MESSAGE_SET_STATE: {
|
|
|
|
|
int r;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-08-24 18:24:46 +02:00
|
|
|
/* First, change the state, because otherwise pa_sink_render() would fail */
|
2008-06-17 18:29:15 +00:00
|
|
|
if ((r = pa_sink_process_msg(o, code, data, offset, chunk)) >= 0) {
|
|
|
|
|
|
2013-07-14 13:39:32 +03:00
|
|
|
stream_cork_within_thread(u, u->sink->thread_info.state == PA_SINK_SUSPENDED);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2013-07-14 13:39:32 +03:00
|
|
|
if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
|
2007-10-29 16:54:16 +00:00
|
|
|
send_data(u);
|
2008-06-17 18:29:15 +00:00
|
|
|
}
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
return r;
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
case PA_SINK_MESSAGE_GET_LATENCY: {
|
source/sink: Allow pa_{source, sink}_get_latency_within_thread() to return negative values
The reported latency of source or sink is based on measured initial conditions.
If the conditions contain an error, the estimated latency values may become negative.
This does not indicate that the latency is indeed negative but can be considered
merely an offset error. The current get_latency_in_thread() calls and the
implementations of the PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY messages truncate negative
latencies because they do not make sense from a physical point of view. In fact,
the values are truncated twice, once in the message handler and a second time in
the pa_{source,sink}_get_latency_within_thread() call itself.
This leads to two problems for the latency controller within module-loopback:
- Truncating leads to discontinuities in the latency reports which then trigger
unwanted end to end latency corrections.
- If a large negative port latency offsets is set, the reported latency is always 0,
making it impossible to control the end to end latency at all.
This patch is a pre-condition for solving these problems.
It adds a new flag to pa_{sink,source}_get_latency_within_thread() to allow
negative return values. Truncating is also removed in all implementations of the
PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY message handlers. The allow_negative flag
is set to false for all calls of pa_{sink,source}_get_latency_within_thread()
except when used within PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY. This means that the
original behavior is not altered in most cases. Only if a positive latency offset
is set and the message returns a negative value, the reported latency is smaller
because the values are not truncated twice.
Additionally let PA_SOURCE_MESSAGE_GET_LATENCY return -pa_sink_get_latency_within_thread()
for monitor sources because the source gets the data before it is played.
2017-04-17 19:50:10 +02:00
|
|
|
pa_usec_t yl, yr;
|
|
|
|
|
int64_t *usec = data;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
yl = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
|
2009-04-04 22:56:38 +03:00
|
|
|
yr = pa_smoother_get(u->smoother, pa_rtclock_now());
|
2008-06-17 18:29:15 +00:00
|
|
|
|
source/sink: Allow pa_{source, sink}_get_latency_within_thread() to return negative values
The reported latency of source or sink is based on measured initial conditions.
If the conditions contain an error, the estimated latency values may become negative.
This does not indicate that the latency is indeed negative but can be considered
merely an offset error. The current get_latency_in_thread() calls and the
implementations of the PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY messages truncate negative
latencies because they do not make sense from a physical point of view. In fact,
the values are truncated twice, once in the message handler and a second time in
the pa_{source,sink}_get_latency_within_thread() call itself.
This leads to two problems for the latency controller within module-loopback:
- Truncating leads to discontinuities in the latency reports which then trigger
unwanted end to end latency corrections.
- If a large negative port latency offsets is set, the reported latency is always 0,
making it impossible to control the end to end latency at all.
This patch is a pre-condition for solving these problems.
It adds a new flag to pa_{sink,source}_get_latency_within_thread() to allow
negative return values. Truncating is also removed in all implementations of the
PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY message handlers. The allow_negative flag
is set to false for all calls of pa_{sink,source}_get_latency_within_thread()
except when used within PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY. This means that the
original behavior is not altered in most cases. Only if a positive latency offset
is set and the message returns a negative value, the reported latency is smaller
because the values are not truncated twice.
Additionally let PA_SOURCE_MESSAGE_GET_LATENCY return -pa_sink_get_latency_within_thread()
for monitor sources because the source gets the data before it is played.
2017-04-17 19:50:10 +02:00
|
|
|
*usec = (int64_t)yl - yr;
|
2008-06-17 18:29:15 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
case SINK_MESSAGE_REQUEST:
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(offset > 0);
|
|
|
|
|
u->requested_bytes += (size_t) offset;
|
2006-08-26 19:00:22 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
|
2007-10-29 16:54:16 +00:00
|
|
|
send_data(u);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
case SINK_MESSAGE_REMOTE_SUSPEND:
|
|
|
|
|
|
|
|
|
|
stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SINK_MESSAGE_UPDATE_LATENCY: {
|
|
|
|
|
pa_usec_t y;
|
|
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
y = pa_bytes_to_usec((uint64_t) u->counter, &u->sink->sample_spec);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
if (y > (pa_usec_t) offset)
|
2008-08-19 22:39:54 +02:00
|
|
|
y -= (pa_usec_t) offset;
|
2008-06-17 18:29:15 +00:00
|
|
|
else
|
|
|
|
|
y = 0;
|
|
|
|
|
|
2009-04-04 22:56:38 +03:00
|
|
|
pa_smoother_put(u->smoother, pa_rtclock_now(), y);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* We can access this freely here, since the main thread is waiting for us */
|
|
|
|
|
u->thread_transport_usec = u->transport_usec;
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
case SINK_MESSAGE_POST:
|
|
|
|
|
|
|
|
|
|
/* OK, This might be a bit confusing. This message is
|
|
|
|
|
* delivered to us from the main context -- NOT from the
|
|
|
|
|
* IO thread context where the rest of the messages are
|
|
|
|
|
* dispatched. Yeah, ugly, but I am a lazy bastard. */
|
|
|
|
|
|
|
|
|
|
pa_pstream_send_memblock(u->pstream, u->channel, 0, PA_SEEK_RELATIVE, chunk);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
u->counter_delta += (int64_t) chunk->length;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pa_sink_process_msg(o, code, data, offset, chunk);
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2018-03-13 19:40:36 +02:00
|
|
|
static int sink_set_state_in_main_thread_cb(pa_sink *s, pa_sink_state_t state, pa_suspend_cause_t suspend_cause) {
|
2007-10-29 16:54:16 +00:00
|
|
|
struct userdata *u;
|
|
|
|
|
pa_sink_assert_ref(s);
|
|
|
|
|
u = s->userdata;
|
|
|
|
|
|
2018-02-19 16:48:23 +02:00
|
|
|
/* It may be that only the suspend cause is changing, in which
|
|
|
|
|
* case there's nothing to do. */
|
|
|
|
|
if (state == s->state)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
switch ((pa_sink_state_t) state) {
|
|
|
|
|
|
|
|
|
|
case PA_SINK_SUSPENDED:
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(PA_SINK_IS_OPENED(s->state));
|
2013-06-27 19:28:09 +02:00
|
|
|
stream_cork(u, true);
|
2007-10-29 16:54:16 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_SINK_IDLE:
|
|
|
|
|
case PA_SINK_RUNNING:
|
|
|
|
|
if (s->state == PA_SINK_SUSPENDED)
|
2013-06-27 19:28:09 +02:00
|
|
|
stream_cork(u, false);
|
2007-10-29 16:54:16 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_SINK_UNLINKED:
|
|
|
|
|
case PA_SINK_INIT:
|
2009-01-22 00:25:36 +01:00
|
|
|
case PA_SINK_INVALID_STATE:
|
2007-10-29 16:54:16 +00:00
|
|
|
;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* This function is called from IO context -- except when it is not. */
|
2007-10-29 16:54:16 +00:00
|
|
|
static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
|
|
|
|
|
struct userdata *u = PA_SOURCE(o)->userdata;
|
|
|
|
|
|
|
|
|
|
switch (code) {
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2008-10-11 18:20:06 +01:00
|
|
|
case PA_SOURCE_MESSAGE_SET_STATE: {
|
2008-06-17 18:29:15 +00:00
|
|
|
int r;
|
|
|
|
|
|
2008-07-16 11:28:46 +02:00
|
|
|
if ((r = pa_source_process_msg(o, code, data, offset, chunk)) >= 0)
|
2013-07-14 13:39:32 +03:00
|
|
|
stream_cork_within_thread(u, u->source->thread_info.state == PA_SOURCE_SUSPENDED);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case PA_SOURCE_MESSAGE_GET_LATENCY: {
|
source/sink: Allow pa_{source, sink}_get_latency_within_thread() to return negative values
The reported latency of source or sink is based on measured initial conditions.
If the conditions contain an error, the estimated latency values may become negative.
This does not indicate that the latency is indeed negative but can be considered
merely an offset error. The current get_latency_in_thread() calls and the
implementations of the PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY messages truncate negative
latencies because they do not make sense from a physical point of view. In fact,
the values are truncated twice, once in the message handler and a second time in
the pa_{source,sink}_get_latency_within_thread() call itself.
This leads to two problems for the latency controller within module-loopback:
- Truncating leads to discontinuities in the latency reports which then trigger
unwanted end to end latency corrections.
- If a large negative port latency offsets is set, the reported latency is always 0,
making it impossible to control the end to end latency at all.
This patch is a pre-condition for solving these problems.
It adds a new flag to pa_{sink,source}_get_latency_within_thread() to allow
negative return values. Truncating is also removed in all implementations of the
PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY message handlers. The allow_negative flag
is set to false for all calls of pa_{sink,source}_get_latency_within_thread()
except when used within PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY. This means that the
original behavior is not altered in most cases. Only if a positive latency offset
is set and the message returns a negative value, the reported latency is smaller
because the values are not truncated twice.
Additionally let PA_SOURCE_MESSAGE_GET_LATENCY return -pa_sink_get_latency_within_thread()
for monitor sources because the source gets the data before it is played.
2017-04-17 19:50:10 +02:00
|
|
|
pa_usec_t yr, yl;
|
|
|
|
|
int64_t *usec = data;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2008-10-11 18:20:06 +01:00
|
|
|
yl = pa_bytes_to_usec((uint64_t) u->counter, &PA_SOURCE(o)->sample_spec);
|
2009-04-04 22:56:38 +03:00
|
|
|
yr = pa_smoother_get(u->smoother, pa_rtclock_now());
|
2008-06-17 18:29:15 +00:00
|
|
|
|
source/sink: Allow pa_{source, sink}_get_latency_within_thread() to return negative values
The reported latency of source or sink is based on measured initial conditions.
If the conditions contain an error, the estimated latency values may become negative.
This does not indicate that the latency is indeed negative but can be considered
merely an offset error. The current get_latency_in_thread() calls and the
implementations of the PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY messages truncate negative
latencies because they do not make sense from a physical point of view. In fact,
the values are truncated twice, once in the message handler and a second time in
the pa_{source,sink}_get_latency_within_thread() call itself.
This leads to two problems for the latency controller within module-loopback:
- Truncating leads to discontinuities in the latency reports which then trigger
unwanted end to end latency corrections.
- If a large negative port latency offsets is set, the reported latency is always 0,
making it impossible to control the end to end latency at all.
This patch is a pre-condition for solving these problems.
It adds a new flag to pa_{sink,source}_get_latency_within_thread() to allow
negative return values. Truncating is also removed in all implementations of the
PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY message handlers. The allow_negative flag
is set to false for all calls of pa_{sink,source}_get_latency_within_thread()
except when used within PA_{SINK,SOURCE}_MESSAGE_GET_LATENCY. This means that the
original behavior is not altered in most cases. Only if a positive latency offset
is set and the message returns a negative value, the reported latency is smaller
because the values are not truncated twice.
Additionally let PA_SOURCE_MESSAGE_GET_LATENCY return -pa_sink_get_latency_within_thread()
for monitor sources because the source gets the data before it is played.
2017-04-17 19:50:10 +02:00
|
|
|
*usec = (int64_t)yr - yl;
|
2008-06-17 18:29:15 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-31 00:50:19 +02:00
|
|
|
case SOURCE_MESSAGE_POST: {
|
|
|
|
|
pa_memchunk c;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2009-07-31 00:50:19 +02:00
|
|
|
pa_mcalign_push(u->mcalign, chunk);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-07-31 00:50:19 +02:00
|
|
|
while (pa_mcalign_pop(u->mcalign, &c) >= 0) {
|
|
|
|
|
|
|
|
|
|
if (PA_SOURCE_IS_OPENED(u->source->thread_info.state))
|
|
|
|
|
pa_source_post(u->source, &c);
|
|
|
|
|
|
|
|
|
|
pa_memblock_unref(c.memblock);
|
|
|
|
|
|
|
|
|
|
u->counter += (int64_t) c.length;
|
|
|
|
|
}
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
return 0;
|
2009-07-31 00:50:19 +02:00
|
|
|
}
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
case SOURCE_MESSAGE_REMOTE_SUSPEND:
|
|
|
|
|
|
|
|
|
|
stream_suspend_within_thread(u, !!PA_PTR_TO_UINT(data));
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
case SOURCE_MESSAGE_UPDATE_LATENCY: {
|
|
|
|
|
pa_usec_t y;
|
|
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
y = pa_bytes_to_usec((uint64_t) u->counter, &u->source->sample_spec);
|
2009-03-30 19:13:07 +02:00
|
|
|
y += (pa_usec_t) offset;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-04-04 22:56:38 +03:00
|
|
|
pa_smoother_put(u->smoother, pa_rtclock_now(), y);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* We can access this freely here, since the main thread is waiting for us */
|
|
|
|
|
u->thread_transport_usec = u->transport_usec;
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
2007-10-29 16:54:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pa_source_process_msg(o, code, data, offset, chunk);
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2018-03-13 19:40:36 +02:00
|
|
|
static int source_set_state_in_main_thread_cb(pa_source *s, pa_source_state_t state, pa_suspend_cause_t suspend_cause) {
|
2007-10-29 16:54:16 +00:00
|
|
|
struct userdata *u;
|
|
|
|
|
pa_source_assert_ref(s);
|
|
|
|
|
u = s->userdata;
|
|
|
|
|
|
2018-02-19 16:48:23 +02:00
|
|
|
/* It may be that only the suspend cause is changing, in which
|
|
|
|
|
* case there's nothing to do. */
|
|
|
|
|
if (state == s->state)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
switch ((pa_source_state_t) state) {
|
|
|
|
|
|
|
|
|
|
case PA_SOURCE_SUSPENDED:
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_assert(PA_SOURCE_IS_OPENED(s->state));
|
2013-06-27 19:28:09 +02:00
|
|
|
stream_cork(u, true);
|
2007-10-29 16:54:16 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_SOURCE_IDLE:
|
|
|
|
|
case PA_SOURCE_RUNNING:
|
|
|
|
|
if (s->state == PA_SOURCE_SUSPENDED)
|
2013-06-27 19:28:09 +02:00
|
|
|
stream_cork(u, false);
|
2007-10-29 16:54:16 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PA_SOURCE_UNLINKED:
|
|
|
|
|
case PA_SOURCE_INIT:
|
2015-01-12 23:52:11 +06:00
|
|
|
case PA_SOURCE_INVALID_STATE:
|
2007-10-29 16:54:16 +00:00
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static void thread_func(void *userdata) {
|
|
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Thread starting up");
|
|
|
|
|
|
|
|
|
|
pa_thread_mq_install(&u->thread_mq);
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
int ret;
|
|
|
|
|
|
2008-06-26 02:56:00 +02:00
|
|
|
#ifdef TUNNEL_SINK
|
2012-08-30 16:50:13 +03:00
|
|
|
if (PA_UNLIKELY(u->sink->thread_info.rewind_requested))
|
|
|
|
|
pa_sink_process_rewind(u->sink, 0);
|
2008-06-26 02:56:00 +02:00
|
|
|
#endif
|
|
|
|
|
|
2014-10-28 13:46:37 +01:00
|
|
|
if ((ret = pa_rtpoll_run(u->rtpoll)) < 0)
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
/* If this was no regular exit from the loop we have to continue
|
|
|
|
|
* processing messages until we received PA_MESSAGE_SHUTDOWN */
|
|
|
|
|
pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
|
|
|
|
|
pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
|
|
|
|
|
|
|
|
|
|
finish:
|
|
|
|
|
pa_log_debug("Thread shutting down");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_request(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
uint32_t bytes, channel;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(command == PA_COMMAND_REQUEST);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &channel) < 0 ||
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_tagstruct_getu32(t, &bytes) < 0) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Invalid protocol reply");
|
|
|
|
|
goto fail;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (channel != u->channel) {
|
2009-06-17 03:45:14 +02:00
|
|
|
pa_log("Received data for invalid channel");
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
|
2007-10-29 16:54:16 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void stream_get_latency_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2004-09-28 23:49:54 +00:00
|
|
|
struct userdata *u = userdata;
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_usec_t sink_usec, source_usec;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool playing;
|
2006-03-04 21:30:29 +00:00
|
|
|
int64_t write_index, read_index;
|
2004-09-28 23:49:54 +00:00
|
|
|
struct timeval local, remote, now;
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_sample_spec *ss;
|
|
|
|
|
int64_t delay;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
|
|
|
|
if (command == PA_COMMAND_ERROR)
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to get latency.");
|
2004-09-28 23:49:54 +00:00
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-04-13 18:55:55 +00:00
|
|
|
if (pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
|
2004-09-28 23:49:54 +00:00
|
|
|
pa_tagstruct_get_usec(t, &source_usec) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &playing) < 0 ||
|
|
|
|
|
pa_tagstruct_get_timeval(t, &local) < 0 ||
|
|
|
|
|
pa_tagstruct_get_timeval(t, &remote) < 0 ||
|
2006-03-04 21:30:29 +00:00
|
|
|
pa_tagstruct_gets64(t, &write_index) < 0 ||
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_tagstruct_gets64(t, &read_index) < 0) {
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Invalid reply.");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
if (u->version >= 13) {
|
|
|
|
|
uint64_t underrun_for = 0, playing_for = 0;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu64(t, &underrun_for) < 0 ||
|
|
|
|
|
pa_tagstruct_getu64(t, &playing_for) < 0) {
|
|
|
|
|
pa_log("Invalid reply.");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_log("Invalid reply.");
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (tag < u->ignore_latency_before) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-10 17:51:06 +00:00
|
|
|
pa_gettimeofday(&now);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Calculate transport usec */
|
2004-09-29 17:38:45 +00:00
|
|
|
if (pa_timeval_cmp(&local, &remote) < 0 && pa_timeval_cmp(&remote, &now)) {
|
2004-09-28 23:49:54 +00:00
|
|
|
/* local and remote seem to have synchronized clocks */
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2008-06-17 18:29:15 +00:00
|
|
|
u->transport_usec = pa_timeval_diff(&remote, &local);
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
2008-06-17 18:29:15 +00:00
|
|
|
u->transport_usec = pa_timeval_diff(&now, &remote);
|
2007-01-04 13:43:45 +00:00
|
|
|
#endif
|
2004-09-29 17:38:45 +00:00
|
|
|
} else
|
2008-06-17 18:29:15 +00:00
|
|
|
u->transport_usec = pa_timeval_diff(&now, &local)/2;
|
2004-09-29 17:38:45 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* First, take the device's delay */
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2008-06-17 18:29:15 +00:00
|
|
|
delay = (int64_t) sink_usec;
|
|
|
|
|
ss = &u->sink->sample_spec;
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
2008-06-17 18:29:15 +00:00
|
|
|
delay = (int64_t) source_usec;
|
|
|
|
|
ss = &u->source->sample_spec;
|
2007-10-29 16:54:16 +00:00
|
|
|
#endif
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Add the length of our server-side buffer */
|
|
|
|
|
if (write_index >= read_index)
|
2008-08-19 22:39:54 +02:00
|
|
|
delay += (int64_t) pa_bytes_to_usec((uint64_t) (write_index-read_index), ss);
|
2008-06-17 18:29:15 +00:00
|
|
|
else
|
2008-08-19 22:39:54 +02:00
|
|
|
delay -= (int64_t) pa_bytes_to_usec((uint64_t) (read_index-write_index), ss);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
/* Our measurements are already out of date, hence correct by the *
|
|
|
|
|
* transport latency */
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2009-03-30 19:13:07 +02:00
|
|
|
delay -= (int64_t) u->transport_usec;
|
2008-06-17 18:29:15 +00:00
|
|
|
#else
|
2009-03-30 19:13:07 +02:00
|
|
|
delay += (int64_t) u->transport_usec;
|
2008-06-17 18:29:15 +00:00
|
|
|
#endif
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Now correct by what we have have read/written since we requested the update */
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2008-08-19 22:39:54 +02:00
|
|
|
delay += (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);
|
2007-10-29 16:54:16 +00:00
|
|
|
#else
|
2008-08-19 22:39:54 +02:00
|
|
|
delay -= (int64_t) pa_bytes_to_usec((uint64_t) u->counter_delta, ss);
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_asyncmsgq_send(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
|
|
|
|
|
#else
|
|
|
|
|
pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_UPDATE_LATENCY, 0, delay, NULL);
|
|
|
|
|
#endif
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2004-09-28 23:49:54 +00:00
|
|
|
static void request_latency(struct userdata *u) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
2004-09-28 23:49:54 +00:00
|
|
|
struct timeval now;
|
|
|
|
|
uint32_t tag;
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(u);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-01-04 13:43:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2004-09-28 23:49:54 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_GET_PLAYBACK_LATENCY);
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
|
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_GET_RECORD_LATENCY);
|
|
|
|
|
#endif
|
2004-09-28 23:49:54 +00:00
|
|
|
pa_tagstruct_putu32(t, tag = u->ctag++);
|
|
|
|
|
pa_tagstruct_putu32(t, u->channel);
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_tagstruct_put_timeval(t, pa_gettimeofday(&now));
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-28 23:49:54 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, stream_get_latency_callback, u, NULL);
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
u->ignore_latency_before = tag;
|
2007-10-29 16:54:16 +00:00
|
|
|
u->counter_delta = 0;
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2009-04-05 02:13:43 +03:00
|
|
|
static void timeout_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
|
2006-03-05 20:18:04 +00:00
|
|
|
struct userdata *u = userdata;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(m);
|
|
|
|
|
pa_assert(e);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
request_latency(u);
|
|
|
|
|
|
2009-04-05 02:13:43 +03:00
|
|
|
pa_core_rttime_restart(u->core, e, pa_rtclock_now() + LATENCY_INTERVAL);
|
2007-10-29 16:54:16 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2007-10-30 01:50:22 +00:00
|
|
|
static void update_description(struct userdata *u) {
|
|
|
|
|
char *d;
|
2007-10-30 02:35:00 +00:00
|
|
|
char un[128], hn[128];
|
|
|
|
|
pa_tagstruct *t;
|
2007-10-30 01:50:22 +00:00
|
|
|
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
if (!u->server_fqdn || !u->user_name || !u->device_description)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-10-30 02:35:00 +00:00
|
|
|
d = pa_sprintf_malloc("%s on %s@%s", u->device_description, u->user_name, u->server_fqdn);
|
2007-10-30 01:50:22 +00:00
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_sink_set_description(u->sink, d);
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_proplist_sets(u->sink->proplist, "tunnel.remote.user", u->user_name);
|
|
|
|
|
pa_proplist_sets(u->sink->proplist, "tunnel.remote.fqdn", u->server_fqdn);
|
|
|
|
|
pa_proplist_sets(u->sink->proplist, "tunnel.remote.description", u->device_description);
|
2007-10-30 01:50:22 +00:00
|
|
|
#else
|
|
|
|
|
pa_source_set_description(u->source, d);
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_proplist_sets(u->source->proplist, "tunnel.remote.user", u->user_name);
|
|
|
|
|
pa_proplist_sets(u->source->proplist, "tunnel.remote.fqdn", u->server_fqdn);
|
|
|
|
|
pa_proplist_sets(u->source->proplist, "tunnel.remote.description", u->device_description);
|
2007-10-30 01:50:22 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
pa_xfree(d);
|
2007-10-30 02:35:00 +00:00
|
|
|
|
|
|
|
|
d = pa_sprintf_malloc("%s for %s@%s", u->device_description,
|
|
|
|
|
pa_get_user_name(un, sizeof(un)),
|
|
|
|
|
pa_get_host_name(hn, sizeof(hn)));
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-30 02:35:00 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_SET_PLAYBACK_STREAM_NAME);
|
|
|
|
|
#else
|
|
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_SET_RECORD_STREAM_NAME);
|
|
|
|
|
#endif
|
|
|
|
|
pa_tagstruct_putu32(t, u->ctag++);
|
|
|
|
|
pa_tagstruct_putu32(t, u->channel);
|
|
|
|
|
pa_tagstruct_puts(t, d);
|
|
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
|
|
|
|
|
pa_xfree(d);
|
2007-10-30 01:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void server_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-10-30 01:50:22 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
pa_sample_spec ss;
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_channel_map cm;
|
2007-10-30 01:50:22 +00:00
|
|
|
const char *server_name, *server_version, *user_name, *host_name, *default_sink_name, *default_source_name;
|
|
|
|
|
uint32_t cookie;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
|
|
|
|
if (command == PA_COMMAND_ERROR)
|
|
|
|
|
pa_log("Failed to get info.");
|
|
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-30 01:50:22 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_gets(t, &server_name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &server_version) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &user_name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &host_name) < 0 ||
|
|
|
|
|
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &default_sink_name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &default_source_name) < 0 ||
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_tagstruct_getu32(t, &cookie) < 0 ||
|
2011-03-02 12:41:26 +01:00
|
|
|
(u->version >= 15 && pa_tagstruct_get_channel_map(t, &cm) < 0)) {
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_log("Packet too long");
|
2007-10-30 01:50:22 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_xfree(u->server_fqdn);
|
|
|
|
|
u->server_fqdn = pa_xstrdup(host_name);
|
|
|
|
|
|
|
|
|
|
pa_xfree(u->user_name);
|
|
|
|
|
u->user_name = pa_xstrdup(user_name);
|
|
|
|
|
|
|
|
|
|
update_description(u);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2007-10-30 01:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-18 22:24:24 +02:00
|
|
|
static int read_ports(struct userdata *u, pa_tagstruct *t) {
|
2011-11-25 15:17:11 +01:00
|
|
|
if (u->version >= 16) {
|
|
|
|
|
uint32_t n_ports;
|
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &n_ports)) {
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint32_t j = 0; j < n_ports; j++) {
|
|
|
|
|
uint32_t priority;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_gets(t, &s) < 0 || /* name */
|
|
|
|
|
pa_tagstruct_gets(t, &s) < 0 || /* description */
|
|
|
|
|
pa_tagstruct_getu32(t, &priority) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
2020-04-14 17:42:34 +02:00
|
|
|
if (u->version >= 24) {
|
|
|
|
|
if (pa_tagstruct_getu32(t, &priority) < 0) { /* available */
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
2020-04-14 20:04:00 +02:00
|
|
|
if (u->version >= 34 &&
|
|
|
|
|
(pa_tagstruct_gets(t, &s) < 0 || /* available_group */
|
|
|
|
|
pa_tagstruct_getu32(t, &priority) < 0)) { /* device port type */
|
2020-04-14 17:42:34 +02:00
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
2011-11-25 15:17:11 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_gets(t, &s) < 0) { /* active port */
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2012-01-30 16:47:54 +01:00
|
|
|
static int read_formats(struct userdata *u, pa_tagstruct *t) {
|
|
|
|
|
uint8_t n_formats;
|
|
|
|
|
pa_format_info *format;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu8(t, &n_formats) < 0) { /* no. of formats */
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint8_t j = 0; j < n_formats; j++) {
|
|
|
|
|
format = pa_format_info_new();
|
|
|
|
|
if (pa_tagstruct_get_format_info(t, format)) { /* format info */
|
|
|
|
|
pa_format_info_free(format);
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
return -PA_ERR_PROTOCOL;
|
|
|
|
|
}
|
|
|
|
|
pa_format_info_free(format);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void sink_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-10-30 01:50:22 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
uint32_t idx, owner_module, monitor_source, flags;
|
|
|
|
|
const char *name, *description, *monitor_source_name, *driver;
|
|
|
|
|
pa_sample_spec ss;
|
|
|
|
|
pa_channel_map cm;
|
|
|
|
|
pa_cvolume volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool mute;
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_usec_t latency;
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
|
|
|
|
if (command == PA_COMMAND_ERROR)
|
|
|
|
|
pa_log("Failed to get info.");
|
|
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-30 01:50:22 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &description) < 0 ||
|
|
|
|
|
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
|
|
|
|
pa_tagstruct_get_channel_map(t, &cm) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &owner_module) < 0 ||
|
|
|
|
|
pa_tagstruct_get_cvolume(t, &volume) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &mute) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &monitor_source) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &monitor_source_name) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &latency) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &driver) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &flags) < 0) {
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
2007-10-30 01:50:22 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (u->version >= 13) {
|
|
|
|
|
pa_usec_t configured_latency;
|
|
|
|
|
|
2012-04-13 14:40:32 +03:00
|
|
|
if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_get_usec(t, &configured_latency) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
if (u->version >= 15) {
|
|
|
|
|
pa_volume_t base_volume;
|
|
|
|
|
uint32_t state, n_volume_steps, card;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &state) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &card) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-25 15:17:11 +01:00
|
|
|
if (read_ports(u, t) < 0)
|
|
|
|
|
goto fail;
|
2009-09-20 21:28:23 +02:00
|
|
|
|
2012-01-30 16:47:54 +01:00
|
|
|
if (u->version >= 21 && read_formats(u, t) < 0)
|
|
|
|
|
goto fail;
|
2011-05-15 09:54:17 +05:30
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_log("Packet too long");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-06 01:28:13 +05:30
|
|
|
if (!u->sink_name || !pa_streq(name, u->sink_name))
|
2007-10-30 02:05:53 +00:00
|
|
|
return;
|
|
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_xfree(u->device_description);
|
|
|
|
|
u->device_description = pa_xstrdup(description);
|
|
|
|
|
|
|
|
|
|
update_description(u);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2007-10-30 01:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-10-29 16:54:16 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
uint32_t idx, owner_module, client, sink;
|
|
|
|
|
pa_usec_t buffer_usec, sink_usec;
|
|
|
|
|
const char *name, *driver, *resample_method;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool mute = false;
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_sample_spec sample_spec;
|
|
|
|
|
pa_channel_map channel_map;
|
|
|
|
|
pa_cvolume volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool b;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
|
|
|
|
if (command == PA_COMMAND_ERROR)
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to get info.");
|
2006-03-05 20:18:04 +00:00
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
2006-03-05 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-06 23:28:56 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_getu32(t, &owner_module) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &client) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &sink) < 0 ||
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_tagstruct_get_sample_spec(t, &sample_spec) < 0 ||
|
|
|
|
|
pa_tagstruct_get_channel_map(t, &channel_map) < 0 ||
|
|
|
|
|
pa_tagstruct_get_cvolume(t, &volume) < 0 ||
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_get_usec(t, &buffer_usec) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &sink_usec) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &resample_method) < 0 ||
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_gets(t, &driver) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->version >= 11) {
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &mute) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->version >= 13) {
|
2012-04-13 14:40:32 +03:00
|
|
|
if (pa_tagstruct_get_proplist(t, NULL) < 0) {
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-15 09:54:17 +05:30
|
|
|
if (u->version >= 19) {
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &b) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->version >= 20) {
|
|
|
|
|
if (pa_tagstruct_get_boolean(t, &b) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &b) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->version >= 21) {
|
2011-08-13 13:43:18 +02:00
|
|
|
pa_format_info *format = pa_format_info_new();
|
2011-05-15 09:54:17 +05:30
|
|
|
|
2011-08-13 13:43:18 +02:00
|
|
|
if (pa_tagstruct_get_format_info(t, format) < 0) {
|
|
|
|
|
pa_format_info_free(format);
|
2011-05-15 09:54:17 +05:30
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2011-08-13 13:43:18 +02:00
|
|
|
pa_format_info_free(format);
|
2011-05-15 09:54:17 +05:30
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_log("Packet too long");
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
2006-03-05 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
if (idx != u->device_index)
|
|
|
|
|
return;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(u->sink);
|
|
|
|
|
|
2014-10-23 15:00:29 +02:00
|
|
|
if ((u->version < 11 || mute == u->sink->muted) &&
|
2009-08-19 02:55:02 +02:00
|
|
|
pa_cvolume_equal(&volume, &u->sink->real_volume))
|
2006-03-05 20:18:04 +00:00
|
|
|
return;
|
|
|
|
|
|
2009-08-07 23:55:06 +02:00
|
|
|
pa_sink_volume_changed(u->sink, &volume);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
if (u->version >= 11)
|
2009-08-07 23:55:06 +02:00
|
|
|
pa_sink_mute_changed(u->sink, mute);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2006-03-05 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
#else
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void source_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-10-30 01:50:22 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
uint32_t idx, owner_module, monitor_of_sink, flags;
|
|
|
|
|
const char *name, *description, *monitor_of_sink_name, *driver;
|
|
|
|
|
pa_sample_spec ss;
|
|
|
|
|
pa_channel_map cm;
|
|
|
|
|
pa_cvolume volume;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool mute;
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_usec_t latency, configured_latency;
|
2007-10-30 01:50:22 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
|
|
|
|
if (command == PA_COMMAND_ERROR)
|
|
|
|
|
pa_log("Failed to get info.");
|
|
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-30 01:50:22 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &idx) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &name) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &description) < 0 ||
|
|
|
|
|
pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
|
|
|
|
pa_tagstruct_get_channel_map(t, &cm) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &owner_module) < 0 ||
|
|
|
|
|
pa_tagstruct_get_cvolume(t, &volume) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &mute) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &monitor_of_sink) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &monitor_of_sink_name) < 0 ||
|
|
|
|
|
pa_tagstruct_get_usec(t, &latency) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &driver) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &flags) < 0) {
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
2007-10-30 01:50:22 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (u->version >= 13) {
|
2012-04-13 14:40:32 +03:00
|
|
|
if (pa_tagstruct_get_proplist(t, NULL) < 0 ||
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_get_usec(t, &configured_latency) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
if (u->version >= 15) {
|
|
|
|
|
pa_volume_t base_volume;
|
|
|
|
|
uint32_t state, n_volume_steps, card;
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_volume(t, &base_volume) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &state) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &n_volume_steps) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &card) < 0) {
|
|
|
|
|
|
|
|
|
|
pa_log("Parse failure");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-11-25 15:17:11 +01:00
|
|
|
if (read_ports(u, t) < 0)
|
|
|
|
|
goto fail;
|
2009-09-20 21:36:14 +01:00
|
|
|
|
2012-01-30 16:47:54 +01:00
|
|
|
if (u->version >= 22 && read_formats(u, t) < 0)
|
|
|
|
|
goto fail;
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (!pa_tagstruct_eof(t)) {
|
|
|
|
|
pa_log("Packet too long");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-06 01:28:13 +05:30
|
|
|
if (!u->source_name || !pa_streq(name, u->source_name))
|
2007-10-30 02:05:53 +00:00
|
|
|
return;
|
|
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_xfree(u->device_description);
|
|
|
|
|
u->device_description = pa_xstrdup(description);
|
|
|
|
|
|
|
|
|
|
update_description(u);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2007-10-30 01:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2006-03-05 20:18:04 +00:00
|
|
|
static void request_info(struct userdata *u) {
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
uint32_t tag;
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_GET_SERVER_INFO);
|
|
|
|
|
pa_tagstruct_putu32(t, tag = u->ctag++);
|
|
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, server_info_cb, u, NULL);
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INPUT_INFO);
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_tagstruct_putu32(t, tag = u->ctag++);
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(t, u->device_index);
|
|
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_input_info_cb, u, NULL);
|
2007-10-30 01:50:22 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (u->sink_name) {
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_GET_SINK_INFO);
|
|
|
|
|
pa_tagstruct_putu32(t, tag = u->ctag++);
|
|
|
|
|
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
|
|
|
|
|
pa_tagstruct_puts(t, u->sink_name);
|
|
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, sink_info_cb, u, NULL);
|
|
|
|
|
}
|
2007-10-30 01:50:22 +00:00
|
|
|
#else
|
2008-06-17 18:29:15 +00:00
|
|
|
if (u->source_name) {
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_GET_SOURCE_INFO);
|
|
|
|
|
pa_tagstruct_putu32(t, tag = u->ctag++);
|
|
|
|
|
pa_tagstruct_putu32(t, PA_INVALID_INDEX);
|
|
|
|
|
pa_tagstruct_puts(t, u->source_name);
|
|
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, source_info_cb, u, NULL);
|
|
|
|
|
}
|
2007-10-30 01:50:22 +00:00
|
|
|
#endif
|
2007-10-29 16:54:16 +00:00
|
|
|
}
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void command_subscribe_event(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2007-10-29 16:54:16 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
pa_subscription_event_type_t e;
|
|
|
|
|
uint32_t idx;
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(t);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(command == PA_COMMAND_SUBSCRIBE_EVENT);
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &e) < 0 ||
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_tagstruct_getu32(t, &idx) < 0) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Invalid protocol reply");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2007-10-29 16:54:16 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
if (e != (PA_SUBSCRIPTION_EVENT_SERVER|PA_SUBSCRIPTION_EVENT_CHANGE) &&
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
e != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
|
|
|
|
|
e != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE)
|
|
|
|
|
#else
|
|
|
|
|
e != (PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE)
|
|
|
|
|
#endif
|
|
|
|
|
)
|
2007-10-29 16:54:16 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
request_info(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2006-03-05 20:18:04 +00:00
|
|
|
static void start_subscribe(struct userdata *u) {
|
|
|
|
|
pa_tagstruct *t;
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_SUBSCRIBE);
|
2009-09-08 23:46:23 +02:00
|
|
|
pa_tagstruct_putu32(t, u->ctag++);
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_SUBSCRIPTION_MASK_SERVER|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SINK
|
|
|
|
|
#else
|
|
|
|
|
PA_SUBSCRIPTION_MASK_SOURCE
|
|
|
|
|
#endif
|
|
|
|
|
);
|
|
|
|
|
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
}
|
2006-09-01 00:24:32 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
|
|
|
|
static void create_stream_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = userdata;
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
uint32_t bytes;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
|
|
|
|
if (command != PA_COMMAND_REPLY) {
|
|
|
|
|
if (command == PA_COMMAND_ERROR)
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to create stream.");
|
2004-09-28 22:47:48 +00:00
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-29 16:54:16 +00:00
|
|
|
goto fail;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pa_tagstruct_getu32(t, &u->channel) < 0 ||
|
2006-08-25 22:52:59 +00:00
|
|
|
pa_tagstruct_getu32(t, &u->device_index) < 0
|
2007-01-04 13:43:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2007-10-29 16:54:16 +00:00
|
|
|
|| pa_tagstruct_getu32(t, &bytes) < 0
|
2007-01-04 13:43:45 +00:00
|
|
|
#endif
|
2006-08-25 22:52:59 +00:00
|
|
|
)
|
|
|
|
|
goto parse_error;
|
|
|
|
|
|
|
|
|
|
if (u->version >= 9) {
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2008-06-17 18:29:15 +00:00
|
|
|
if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &u->tlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &u->prebuf) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &u->minreq) < 0)
|
|
|
|
|
goto parse_error;
|
|
|
|
|
#else
|
|
|
|
|
if (pa_tagstruct_getu32(t, &u->maxlength) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &u->fragsize) < 0)
|
|
|
|
|
goto parse_error;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (u->version >= 12) {
|
|
|
|
|
pa_sample_spec ss;
|
|
|
|
|
pa_channel_map cm;
|
|
|
|
|
uint32_t device_index;
|
|
|
|
|
const char *dn;
|
2013-06-27 19:28:09 +02:00
|
|
|
bool suspended;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
if (pa_tagstruct_get_sample_spec(t, &ss) < 0 ||
|
|
|
|
|
pa_tagstruct_get_channel_map(t, &cm) < 0 ||
|
|
|
|
|
pa_tagstruct_getu32(t, &device_index) < 0 ||
|
|
|
|
|
pa_tagstruct_gets(t, &dn) < 0 ||
|
|
|
|
|
pa_tagstruct_get_boolean(t, &suspended) < 0)
|
2006-08-25 22:52:59 +00:00
|
|
|
goto parse_error;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_xfree(u->sink_name);
|
|
|
|
|
u->sink_name = pa_xstrdup(dn);
|
2006-08-25 22:52:59 +00:00
|
|
|
#else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_xfree(u->source_name);
|
|
|
|
|
u->source_name = pa_xstrdup(dn);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->version >= 13) {
|
|
|
|
|
pa_usec_t usec;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (pa_tagstruct_get_usec(t, &usec) < 0)
|
2006-08-25 22:52:59 +00:00
|
|
|
goto parse_error;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* #ifdef TUNNEL_SINK */
|
|
|
|
|
/* pa_sink_set_latency_range(u->sink, usec + MIN_NETWORK_LATENCY_USEC, 0); */
|
|
|
|
|
/* #else */
|
|
|
|
|
/* pa_source_set_latency_range(u->source, usec + MIN_NETWORK_LATENCY_USEC, 0); */
|
|
|
|
|
/* #endif */
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-05-15 09:54:17 +05:30
|
|
|
if (u->version >= 21) {
|
2011-08-13 13:43:18 +02:00
|
|
|
pa_format_info *format = pa_format_info_new();
|
2011-05-15 09:54:17 +05:30
|
|
|
|
2011-08-13 13:43:18 +02:00
|
|
|
if (pa_tagstruct_get_format_info(t, format) < 0) {
|
|
|
|
|
pa_format_info_free(format);
|
2011-05-15 09:54:17 +05:30
|
|
|
goto parse_error;
|
2011-08-13 13:43:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_format_info_free(format);
|
2011-05-15 09:54:17 +05:30
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (!pa_tagstruct_eof(t))
|
|
|
|
|
goto parse_error;
|
|
|
|
|
|
2006-03-05 20:18:04 +00:00
|
|
|
start_subscribe(u);
|
|
|
|
|
request_info(u);
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(!u->time_event);
|
2009-04-05 02:13:43 +03:00
|
|
|
u->time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + LATENCY_INTERVAL, timeout_callback, u);
|
2006-09-01 00:24:32 +00:00
|
|
|
|
2004-09-28 23:49:54 +00:00
|
|
|
request_latency(u);
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_log_debug("Stream created.");
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_asyncmsgq_post(u->sink->asyncmsgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_REQUEST, NULL, bytes, NULL, NULL);
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2006-08-25 22:52:59 +00:00
|
|
|
|
|
|
|
|
return;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-08-25 22:52:59 +00:00
|
|
|
parse_error:
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Invalid reply. (Create stream)");
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2006-01-11 01:17:39 +00:00
|
|
|
static void setup_complete_callback(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = userdata;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *reply;
|
2004-09-28 22:47:48 +00:00
|
|
|
char name[256], un[128], hn[128];
|
2006-03-04 21:30:29 +00:00
|
|
|
pa_cvolume volume;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(pd);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->pdispatch == pd);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2006-03-04 21:30:29 +00:00
|
|
|
if (command != PA_COMMAND_REPLY ||
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_getu32(t, &u->version) < 0 ||
|
|
|
|
|
!pa_tagstruct_eof(t)) {
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
if (command == PA_COMMAND_ERROR)
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to authenticate");
|
2004-09-28 22:47:48 +00:00
|
|
|
else
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Protocol error.");
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
goto fail;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
2006-03-04 21:30:29 +00:00
|
|
|
|
|
|
|
|
/* Minimum supported protocol version */
|
|
|
|
|
if (u->version < 8) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Incompatible protocol version");
|
|
|
|
|
goto fail;
|
2006-03-04 21:30:29 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Starting with protocol version 13 the MSB of the version tag
|
|
|
|
|
reflects if shm is enabled for this connection or not. We don't
|
|
|
|
|
support SHM here at all, so we just ignore this. */
|
|
|
|
|
|
|
|
|
|
if (u->version >= 13)
|
|
|
|
|
u->version &= 0x7FFFFFFFU;
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Protocol version: remote %u, local %u", u->version, PA_PROTOCOL_VERSION);
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_proplist_setf(u->sink->proplist, "tunnel.remote_version", "%u", u->version);
|
|
|
|
|
pa_sink_update_proplist(u->sink, 0, NULL);
|
|
|
|
|
|
2007-10-30 02:35:00 +00:00
|
|
|
pa_snprintf(name, sizeof(name), "%s for %s@%s",
|
|
|
|
|
u->sink_name,
|
|
|
|
|
pa_get_user_name(un, sizeof(un)),
|
|
|
|
|
pa_get_host_name(hn, sizeof(hn)));
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_proplist_setf(u->source->proplist, "tunnel.remote_version", "%u", u->version);
|
|
|
|
|
pa_source_update_proplist(u->source, 0, NULL);
|
|
|
|
|
|
2007-10-30 02:35:00 +00:00
|
|
|
pa_snprintf(name, sizeof(name), "%s for %s@%s",
|
|
|
|
|
u->source_name,
|
|
|
|
|
pa_get_user_name(un, sizeof(un)),
|
|
|
|
|
pa_get_host_name(hn, sizeof(hn)));
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
reply = pa_tagstruct_new();
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_tagstruct_putu32(reply, PA_COMMAND_SET_CLIENT_NAME);
|
2009-09-08 23:46:23 +02:00
|
|
|
pa_tagstruct_putu32(reply, u->ctag++);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
if (u->version >= 13) {
|
|
|
|
|
pa_proplist *pl;
|
|
|
|
|
pl = pa_proplist_new();
|
|
|
|
|
pa_proplist_sets(pl, PA_PROP_APPLICATION_ID, "org.PulseAudio.PulseAudio");
|
|
|
|
|
pa_proplist_sets(pl, PA_PROP_APPLICATION_VERSION, PACKAGE_VERSION);
|
2009-03-30 19:13:07 +02:00
|
|
|
pa_init_proplist(pl);
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_tagstruct_put_proplist(reply, pl);
|
|
|
|
|
pa_proplist_free(pl);
|
|
|
|
|
} else
|
|
|
|
|
pa_tagstruct_puts(reply, "PulseAudio");
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, reply);
|
|
|
|
|
/* We ignore the server's reply here */
|
|
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
reply = pa_tagstruct_new();
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
if (u->version < 13)
|
|
|
|
|
/* Only for older PA versions we need to fill in the maxlength */
|
|
|
|
|
u->maxlength = 4*1024*1024;
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2008-08-19 22:39:54 +02:00
|
|
|
u->tlength = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_TLENGTH_MSEC, &u->sink->sample_spec);
|
|
|
|
|
u->minreq = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_MINREQ_MSEC, &u->sink->sample_spec);
|
2008-06-17 18:29:15 +00:00
|
|
|
u->prebuf = u->tlength;
|
|
|
|
|
#else
|
2008-08-19 22:39:54 +02:00
|
|
|
u->fragsize = (uint32_t) pa_usec_to_bytes(PA_USEC_PER_MSEC * DEFAULT_FRAGSIZE_MSEC, &u->source->sample_spec);
|
2008-06-17 18:29:15 +00:00
|
|
|
#endif
|
|
|
|
|
|
2007-01-04 13:43:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_PLAYBACK_STREAM);
|
|
|
|
|
pa_tagstruct_putu32(reply, tag = u->ctag++);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
if (u->version < 13)
|
|
|
|
|
pa_tagstruct_puts(reply, name);
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_tagstruct_put_sample_spec(reply, &u->sink->sample_spec);
|
2006-03-04 21:30:29 +00:00
|
|
|
pa_tagstruct_put_channel_map(reply, &u->sink->channel_map);
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
|
|
|
|
|
pa_tagstruct_puts(reply, u->sink_name);
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(reply, u->maxlength);
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_put_boolean(reply, !PA_SINK_IS_OPENED(u->sink->state));
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(reply, u->tlength);
|
|
|
|
|
pa_tagstruct_putu32(reply, u->prebuf);
|
|
|
|
|
pa_tagstruct_putu32(reply, u->minreq);
|
2006-03-04 21:30:29 +00:00
|
|
|
pa_tagstruct_putu32(reply, 0);
|
|
|
|
|
pa_cvolume_reset(&volume, u->sink->sample_spec.channels);
|
|
|
|
|
pa_tagstruct_put_cvolume(reply, &volume);
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
|
|
|
|
pa_tagstruct_putu32(reply, PA_COMMAND_CREATE_RECORD_STREAM);
|
|
|
|
|
pa_tagstruct_putu32(reply, tag = u->ctag++);
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
if (u->version < 13)
|
|
|
|
|
pa_tagstruct_puts(reply, name);
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
pa_tagstruct_put_sample_spec(reply, &u->source->sample_spec);
|
2006-03-04 21:30:29 +00:00
|
|
|
pa_tagstruct_put_channel_map(reply, &u->source->channel_map);
|
2004-09-29 17:38:45 +00:00
|
|
|
pa_tagstruct_putu32(reply, PA_INVALID_INDEX);
|
|
|
|
|
pa_tagstruct_puts(reply, u->source_name);
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(reply, u->maxlength);
|
2018-06-26 16:25:58 +03:00
|
|
|
pa_tagstruct_put_boolean(reply, !PA_SOURCE_IS_OPENED(u->source->state));
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(reply, u->fragsize);
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2008-01-04 14:57:31 +00:00
|
|
|
if (u->version >= 12) {
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* no_remap */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* no_remix */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* fix_format */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* fix_rate */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* fix_channels */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, true); /* no_move */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* variable_rate */
|
2008-06-17 18:29:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->version >= 13) {
|
|
|
|
|
pa_proplist *pl;
|
|
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* start muted/peak detect*/
|
|
|
|
|
pa_tagstruct_put_boolean(reply, true); /* adjust_latency */
|
2008-06-17 18:29:15 +00:00
|
|
|
|
|
|
|
|
pl = pa_proplist_new();
|
|
|
|
|
pa_proplist_sets(pl, PA_PROP_MEDIA_NAME, name);
|
|
|
|
|
pa_proplist_sets(pl, PA_PROP_MEDIA_ROLE, "abstract");
|
|
|
|
|
pa_tagstruct_put_proplist(reply, pl);
|
|
|
|
|
pa_proplist_free(pl);
|
|
|
|
|
|
|
|
|
|
#ifndef TUNNEL_SINK
|
|
|
|
|
pa_tagstruct_putu32(reply, PA_INVALID_INDEX); /* direct on input */
|
|
|
|
|
#endif
|
2008-01-04 14:57:31 +00:00
|
|
|
}
|
|
|
|
|
|
2008-10-05 23:02:25 +02:00
|
|
|
if (u->version >= 14) {
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* volume_set */
|
2008-10-05 23:02:25 +02:00
|
|
|
#endif
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, true); /* early rquests */
|
2008-10-05 23:02:25 +02:00
|
|
|
}
|
|
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
if (u->version >= 15) {
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* muted_set */
|
2009-03-30 19:13:07 +02:00
|
|
|
#endif
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* don't inhibit auto suspend */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* fail on suspend */
|
2009-03-30 19:13:07 +02:00
|
|
|
}
|
|
|
|
|
|
2011-03-20 14:09:14 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
if (u->version >= 17)
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* relative volume */
|
2011-03-20 14:09:14 +00:00
|
|
|
|
2011-03-20 23:01:13 +01:00
|
|
|
if (u->version >= 18)
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
|
2011-03-20 14:09:14 +00:00
|
|
|
#endif
|
|
|
|
|
|
2011-05-15 09:54:17 +05:30
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
if (u->version >= 21) {
|
|
|
|
|
/* We're not using the extended API, so n_formats = 0 and that's that */
|
2011-08-13 13:43:18 +02:00
|
|
|
pa_tagstruct_putu8(reply, 0);
|
2011-05-15 09:54:17 +05:30
|
|
|
}
|
2012-01-30 16:47:54 +01:00
|
|
|
#else
|
|
|
|
|
if (u->version >= 22) {
|
|
|
|
|
/* We're not using the extended API, so n_formats = 0 and that's that */
|
|
|
|
|
pa_tagstruct_putu8(reply, 0);
|
|
|
|
|
pa_cvolume_reset(&volume, u->source->sample_spec.channels);
|
|
|
|
|
pa_tagstruct_put_cvolume(reply, &volume);
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_tagstruct_put_boolean(reply, false); /* muted */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* volume_set */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* muted_set */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* relative volume */
|
|
|
|
|
pa_tagstruct_put_boolean(reply, false); /* passthrough stream */
|
2012-01-30 16:47:54 +01:00
|
|
|
}
|
2011-05-15 09:54:17 +05:30
|
|
|
#endif
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, reply);
|
2006-04-24 19:29:15 +00:00
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, create_stream_callback, u, NULL);
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_log_debug("Connection authenticated, creating stream ...");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
fail:
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2006-01-11 01:17:39 +00:00
|
|
|
static void pstream_die_callback(pa_pstream *p, void *userdata) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = userdata;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
|
|
|
|
|
pa_log_warn("Stream died.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2016-03-13 01:12:18 +02:00
|
|
|
static void pstream_packet_callback(pa_pstream *p, pa_packet *packet, pa_cmsg_ancil_data *ancil_data, void *userdata) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = userdata;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(packet);
|
|
|
|
|
pa_assert(u);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2014-08-06 07:48:19 +05:30
|
|
|
if (pa_pdispatch_run(u->pdispatch, packet, ancil_data, u) < 0) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Invalid packet");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2007-10-29 16:54:16 +00:00
|
|
|
return;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifndef TUNNEL_SINK
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2007-10-29 16:54:16 +00:00
|
|
|
static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t offset, pa_seek_mode_t seek, const pa_memchunk *chunk, void *userdata) {
|
2004-09-29 17:38:45 +00:00
|
|
|
struct userdata *u = userdata;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(p);
|
|
|
|
|
pa_assert(chunk);
|
|
|
|
|
pa_assert(u);
|
2004-09-29 17:38:45 +00:00
|
|
|
|
|
|
|
|
if (channel != u->channel) {
|
2009-06-17 03:45:14 +02:00
|
|
|
pa_log("Received memory block on bad channel.");
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-29 17:38:45 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_asyncmsgq_send(u->source->asyncmsgq, PA_MSGOBJECT(u->source), SOURCE_MESSAGE_POST, PA_UINT_TO_PTR(seek), offset, chunk);
|
|
|
|
|
|
2008-08-19 22:39:54 +02:00
|
|
|
u->counter_delta += (int64_t) chunk->length;
|
2004-09-29 17:38:45 +00:00
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2006-01-11 01:17:39 +00:00
|
|
|
static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = userdata;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_tagstruct *t;
|
2004-09-28 22:47:48 +00:00
|
|
|
uint32_t tag;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(sc);
|
|
|
|
|
pa_assert(u);
|
|
|
|
|
pa_assert(u->client == sc);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
|
|
|
|
pa_socket_client_unref(u->client);
|
|
|
|
|
u->client = NULL;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
if (!io) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Connection failed: %s", pa_cstrerror(errno));
|
2013-06-27 19:28:09 +02:00
|
|
|
pa_module_unload_request(u->module, true);
|
2004-09-28 22:47:48 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-18 19:55:18 +00:00
|
|
|
u->pstream = pa_pstream_new(u->core->mainloop, io, u->core->mempool);
|
2013-06-27 19:28:09 +02:00
|
|
|
u->pdispatch = pa_pdispatch_new(u->core->mainloop, true, command_table, PA_COMMAND_MAX);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
|
|
|
|
pa_pstream_set_die_callback(u->pstream, pstream_die_callback, u);
|
2011-12-12 22:36:39 +00:00
|
|
|
pa_pstream_set_receive_packet_callback(u->pstream, pstream_packet_callback, u);
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifndef TUNNEL_SINK
|
2011-12-12 22:36:39 +00:00
|
|
|
pa_pstream_set_receive_memblock_callback(u->pstream, pstream_memblock_callback, u);
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_AUTH);
|
|
|
|
|
pa_tagstruct_putu32(t, tag = u->ctag++);
|
2006-03-04 21:30:29 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_PROTOCOL_VERSION);
|
2008-08-03 16:44:38 +02:00
|
|
|
|
|
|
|
|
pa_tagstruct_put_arbitrary(t, pa_auth_cookie_read(u->auth_cookie, PA_NATIVE_COOKIE_LENGTH), PA_NATIVE_COOKIE_LENGTH);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef HAVE_CREDS
|
|
|
|
|
{
|
|
|
|
|
pa_creds ucred;
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
if (pa_iochannel_creds_supported(io))
|
|
|
|
|
pa_iochannel_creds_enable(io);
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
ucred.uid = getuid();
|
|
|
|
|
ucred.gid = getgid();
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_pstream_send_tagstruct_with_creds(u->pstream, t, &ucred);
|
2004-09-28 23:49:54 +00:00
|
|
|
}
|
2007-10-29 16:54:16 +00:00
|
|
|
#else
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
2007-10-29 16:54:16 +00:00
|
|
|
#endif
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_pdispatch_register_reply(u->pdispatch, tag, DEFAULT_TIMEOUT, setup_complete_callback, u, NULL);
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Connection established, authenticating ...");
|
2006-03-05 20:18:04 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2009-01-27 04:39:07 +01:00
|
|
|
static void sink_set_volume(pa_sink *sink) {
|
2006-03-05 20:18:04 +00:00
|
|
|
struct userdata *u;
|
|
|
|
|
pa_tagstruct *t;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(sink);
|
2006-03-05 20:18:04 +00:00
|
|
|
u = sink->userdata;
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
|
2009-09-08 23:46:23 +02:00
|
|
|
pa_tagstruct_putu32(t, u->ctag++);
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(t, u->device_index);
|
2009-08-19 02:55:02 +02:00
|
|
|
pa_tagstruct_put_cvolume(t, &sink->real_volume);
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
}
|
2006-08-26 19:00:22 +00:00
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
/* Called from main context */
|
2009-01-27 04:39:07 +01:00
|
|
|
static void sink_set_mute(pa_sink *sink) {
|
2006-03-05 20:18:04 +00:00
|
|
|
struct userdata *u;
|
|
|
|
|
pa_tagstruct *t;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(sink);
|
|
|
|
|
u = sink->userdata;
|
|
|
|
|
pa_assert(u);
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
if (u->version < 11)
|
2009-01-27 04:39:07 +01:00
|
|
|
return;
|
2006-03-05 20:18:04 +00:00
|
|
|
|
2014-10-23 16:09:45 +02:00
|
|
|
t = pa_tagstruct_new();
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
|
2009-09-08 23:46:23 +02:00
|
|
|
pa_tagstruct_putu32(t, u->ctag++);
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_tagstruct_putu32(t, u->device_index);
|
2014-04-15 13:56:12 +03:00
|
|
|
pa_tagstruct_put_boolean(t, sink->muted);
|
2006-03-05 20:18:04 +00:00
|
|
|
pa_pstream_send_tagstruct(u->pstream, t);
|
|
|
|
|
}
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
int pa__init(pa_module*m) {
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_modargs *ma = NULL;
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata *u = NULL;
|
2013-07-11 13:57:41 +02:00
|
|
|
char *server = NULL;
|
|
|
|
|
pa_strlist *server_list = NULL;
|
2006-01-11 01:17:39 +00:00
|
|
|
pa_sample_spec ss;
|
2006-04-26 15:40:14 +00:00
|
|
|
pa_channel_map map;
|
2008-06-17 18:29:15 +00:00
|
|
|
char *dn = NULL;
|
2008-05-15 23:34:41 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_sink_new_data data;
|
|
|
|
|
#else
|
|
|
|
|
pa_source_new_data data;
|
|
|
|
|
#endif
|
2013-07-11 13:57:41 +02:00
|
|
|
bool automatic;
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
xcb_connection_t *xcb = NULL;
|
|
|
|
|
#endif
|
|
|
|
|
const char *cookie_path;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_assert(m);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
|
|
|
|
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Failed to parse module arguments");
|
2004-09-28 23:49:54 +00:00
|
|
|
goto fail;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2008-06-17 18:29:15 +00:00
|
|
|
m->userdata = u = pa_xnew0(struct userdata, 1);
|
2007-10-29 16:54:16 +00:00
|
|
|
u->core = m->core;
|
2008-06-20 22:32:41 +02:00
|
|
|
u->module = m;
|
2004-09-28 22:47:48 +00:00
|
|
|
u->client = NULL;
|
|
|
|
|
u->pdispatch = NULL;
|
|
|
|
|
u->pstream = NULL;
|
2004-09-29 17:38:45 +00:00
|
|
|
u->server_name = NULL;
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));;
|
2004-09-28 22:47:48 +00:00
|
|
|
u->sink = NULL;
|
2004-09-29 17:38:45 +00:00
|
|
|
u->requested_bytes = 0;
|
|
|
|
|
#else
|
|
|
|
|
u->source_name = pa_xstrdup(pa_modargs_get_value(ma, "source", NULL));;
|
|
|
|
|
u->source = NULL;
|
|
|
|
|
#endif
|
2009-04-05 02:26:02 +02:00
|
|
|
u->smoother = pa_smoother_new(
|
|
|
|
|
PA_USEC_PER_SEC,
|
|
|
|
|
PA_USEC_PER_SEC*2,
|
2013-06-27 19:28:09 +02:00
|
|
|
true,
|
|
|
|
|
true,
|
2009-04-05 02:26:02 +02:00
|
|
|
10,
|
2009-04-04 22:56:38 +03:00
|
|
|
pa_rtclock_now(),
|
2013-06-27 19:28:09 +02:00
|
|
|
false);
|
2004-09-28 22:47:48 +00:00
|
|
|
u->ctag = 1;
|
|
|
|
|
u->device_index = u->channel = PA_INVALID_INDEX;
|
2004-11-07 20:48:46 +00:00
|
|
|
u->time_event = NULL;
|
2008-06-17 18:29:15 +00:00
|
|
|
u->ignore_latency_before = 0;
|
2009-03-30 19:13:07 +02:00
|
|
|
u->transport_usec = u->thread_transport_usec = 0;
|
2013-06-27 19:28:09 +02:00
|
|
|
u->remote_suspended = u->remote_corked = false;
|
2008-06-17 18:29:15 +00:00
|
|
|
u->counter = u->counter_delta = 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
u->rtpoll = pa_rtpoll_new();
|
2016-09-13 18:43:38 +03:00
|
|
|
|
|
|
|
|
if (pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll) < 0) {
|
|
|
|
|
pa_log("pa_thread_mq_init() failed.");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
if (pa_modargs_get_value_boolean(ma, "auto", &automatic) < 0) {
|
|
|
|
|
pa_log("Failed to parse argument \"auto\".");
|
2004-09-28 22:47:48 +00:00
|
|
|
goto fail;
|
2013-07-11 13:57:41 +02:00
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
cookie_path = pa_modargs_get_value(ma, "cookie", NULL);
|
|
|
|
|
server = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL));
|
|
|
|
|
|
|
|
|
|
if (automatic) {
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
/* Need an X11 connection to get root properties */
|
|
|
|
|
if (getenv("DISPLAY") != NULL) {
|
|
|
|
|
if (!(xcb = xcb_connect(getenv("DISPLAY"), NULL)))
|
|
|
|
|
pa_log("xcb_connect() failed");
|
|
|
|
|
else {
|
|
|
|
|
if (xcb_connection_has_error(xcb)) {
|
|
|
|
|
pa_log("xcb_connection_has_error() returned true");
|
|
|
|
|
xcb_disconnect(xcb);
|
|
|
|
|
xcb = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Figure out the cookie the same way a normal client would */
|
2014-03-13 13:04:55 +02:00
|
|
|
if (!cookie_path)
|
2013-07-11 13:57:41 +02:00
|
|
|
cookie_path = getenv(ENV_COOKIE_FILE);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
if (!cookie_path && xcb) {
|
|
|
|
|
char t[1024];
|
|
|
|
|
if (pa_x11_get_prop(xcb, 0, "PULSE_COOKIE", t, sizeof(t))) {
|
|
|
|
|
uint8_t cookie[PA_NATIVE_COOKIE_LENGTH];
|
|
|
|
|
|
|
|
|
|
if (pa_parsehex(t, cookie, sizeof(cookie)) != sizeof(cookie))
|
|
|
|
|
pa_log("Failed to parse cookie data");
|
|
|
|
|
else {
|
|
|
|
|
if (!(u->auth_cookie = pa_auth_cookie_create(u->core, cookie, sizeof(cookie))))
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Same thing for the server name */
|
|
|
|
|
if (!server)
|
|
|
|
|
server = pa_xstrdup(getenv(ENV_DEFAULT_SERVER));
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
if (!server && xcb) {
|
|
|
|
|
char t[1024];
|
|
|
|
|
if (pa_x11_get_prop(xcb, 0, "PULSE_SERVER", t, sizeof(t)))
|
|
|
|
|
server = pa_xstrdup(t);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* Also determine the default sink/source on the other server */
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
if (!u->sink_name)
|
|
|
|
|
u->sink_name = pa_xstrdup(getenv(ENV_DEFAULT_SINK));
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
if (!u->sink_name && xcb) {
|
|
|
|
|
char t[1024];
|
|
|
|
|
if (pa_x11_get_prop(xcb, 0, "PULSE_SINK", t, sizeof(t)))
|
|
|
|
|
u->sink_name = pa_xstrdup(t);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#else
|
|
|
|
|
if (!u->source_name)
|
|
|
|
|
u->source_name = pa_xstrdup(getenv(ENV_DEFAULT_SOURCE));
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
if (!u->source_name && xcb) {
|
|
|
|
|
char t[1024];
|
|
|
|
|
if (pa_x11_get_prop(xcb, 0, "PULSE_SOURCE", t, sizeof(t)))
|
|
|
|
|
u->source_name = pa_xstrdup(t);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cookie_path && !u->auth_cookie)
|
|
|
|
|
cookie_path = PA_NATIVE_COOKIE_FILE;
|
|
|
|
|
|
|
|
|
|
if (cookie_path) {
|
|
|
|
|
if (!(u->auth_cookie = pa_auth_cookie_get(u->core, cookie_path, true, PA_NATIVE_COOKIE_LENGTH)))
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (server) {
|
|
|
|
|
if (!(server_list = pa_strlist_parse(server))) {
|
|
|
|
|
pa_log("Invalid server specified.");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
char *ufn;
|
|
|
|
|
|
|
|
|
|
if (!automatic) {
|
|
|
|
|
pa_log("No server specified.");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_log("No server address found. Attempting default local sockets.");
|
|
|
|
|
|
|
|
|
|
/* The system wide instance via PF_LOCAL */
|
|
|
|
|
server_list = pa_strlist_prepend(server_list, PA_SYSTEM_RUNTIME_PATH PA_PATH_SEP PA_NATIVE_DEFAULT_UNIX_SOCKET);
|
|
|
|
|
|
|
|
|
|
/* The user instance via PF_LOCAL */
|
|
|
|
|
if ((ufn = pa_runtime_path(PA_NATIVE_DEFAULT_UNIX_SOCKET))) {
|
|
|
|
|
server_list = pa_strlist_prepend(server_list, ufn);
|
|
|
|
|
pa_xfree(ufn);
|
|
|
|
|
}
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
ss = m->core->default_sample_spec;
|
2009-02-21 16:32:42 +01:00
|
|
|
map = m->core->default_channel_map;
|
2006-05-16 23:47:38 +00:00
|
|
|
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_log("Invalid sample format specification");
|
2004-09-28 22:47:48 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
for (;;) {
|
|
|
|
|
server_list = pa_strlist_pop(server_list, &u->server_name);
|
|
|
|
|
|
|
|
|
|
if (!u->server_name) {
|
|
|
|
|
pa_log("Failed to connect to server '%s'", server);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_log_debug("Trying to connect to %s...", u->server_name);
|
|
|
|
|
|
|
|
|
|
if (!(u->client = pa_socket_client_new_string(m->core->mainloop, true, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
|
|
|
|
|
pa_xfree(u->server_name);
|
|
|
|
|
u->server_name = NULL;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_socket_client_set_callback(u->client, on_connection, u);
|
2004-09-29 17:38:45 +00:00
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
2006-08-25 22:52:59 +00:00
|
|
|
|
|
|
|
|
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "sink_name", NULL))))
|
2011-01-16 14:34:11 +00:00
|
|
|
dn = pa_sprintf_malloc("tunnel-sink.%s", u->server_name);
|
2006-08-25 22:52:59 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_sink_new_data_init(&data);
|
|
|
|
|
data.driver = __FILE__;
|
|
|
|
|
data.module = m;
|
2013-07-11 13:57:41 +02:00
|
|
|
data.namereg_fail = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_sink_new_data_set_name(&data, dn);
|
|
|
|
|
pa_sink_new_data_set_sample_spec(&data, &ss);
|
|
|
|
|
pa_sink_new_data_set_channel_map(&data, &map);
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->sink_name), u->sink_name ? " on " : "", u->server_name);
|
|
|
|
|
pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
|
|
|
|
|
if (u->sink_name)
|
|
|
|
|
pa_proplist_sets(data.proplist, "tunnel.remote.sink", u->sink_name);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-05-28 02:39:22 +02:00
|
|
|
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
|
|
|
|
|
pa_log("Invalid properties");
|
|
|
|
|
pa_sink_new_data_done(&data);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-05 23:44:06 +01:00
|
|
|
u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY);
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_sink_new_data_done(&data);
|
|
|
|
|
|
|
|
|
|
if (!u->sink) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to create sink.");
|
2004-09-28 22:47:48 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
u->sink->parent.process_msg = sink_process_msg;
|
2004-09-28 22:47:48 +00:00
|
|
|
u->sink->userdata = u;
|
2018-03-13 19:40:36 +02:00
|
|
|
u->sink->set_state_in_main_thread = sink_set_state_in_main_thread_cb;
|
2011-07-17 15:29:29 +01:00
|
|
|
pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
|
|
|
|
|
pa_sink_set_set_mute_callback(u->sink, sink_set_mute);
|
2007-10-29 16:54:16 +00:00
|
|
|
|
2013-06-27 19:28:09 +02:00
|
|
|
u->sink->refresh_volume = u->sink->refresh_muted = false;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* pa_sink_set_latency_range(u->sink, MIN_NETWORK_LATENCY_USEC, 0); */
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq);
|
|
|
|
|
pa_sink_set_rtpoll(u->sink, u->rtpoll);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
2006-08-25 22:52:59 +00:00
|
|
|
|
|
|
|
|
if (!(dn = pa_xstrdup(pa_modargs_get_value(ma, "source_name", NULL))))
|
2011-01-16 14:34:11 +00:00
|
|
|
dn = pa_sprintf_malloc("tunnel-source.%s", u->server_name);
|
2006-08-25 22:52:59 +00:00
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_source_new_data_init(&data);
|
|
|
|
|
data.driver = __FILE__;
|
|
|
|
|
data.module = m;
|
2013-07-11 13:57:41 +02:00
|
|
|
data.namereg_fail = false;
|
2008-05-15 23:34:41 +00:00
|
|
|
pa_source_new_data_set_name(&data, dn);
|
|
|
|
|
pa_source_new_data_set_sample_spec(&data, &ss);
|
|
|
|
|
pa_source_new_data_set_channel_map(&data, &map);
|
2008-06-17 18:29:15 +00:00
|
|
|
pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "%s%s%s", pa_strempty(u->source_name), u->source_name ? " on " : "", u->server_name);
|
|
|
|
|
pa_proplist_sets(data.proplist, "tunnel.remote.server", u->server_name);
|
|
|
|
|
if (u->source_name)
|
|
|
|
|
pa_proplist_sets(data.proplist, "tunnel.remote.source", u->source_name);
|
2008-05-15 23:34:41 +00:00
|
|
|
|
2009-05-28 02:39:22 +02:00
|
|
|
if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
|
|
|
|
|
pa_log("Invalid properties");
|
|
|
|
|
pa_source_new_data_done(&data);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2008-05-15 23:34:41 +00:00
|
|
|
u->source = pa_source_new(m->core, &data, PA_SOURCE_NETWORK|PA_SOURCE_LATENCY);
|
|
|
|
|
pa_source_new_data_done(&data);
|
|
|
|
|
|
|
|
|
|
if (!u->source) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to create source.");
|
2004-09-29 17:38:45 +00:00
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
u->source->parent.process_msg = source_process_msg;
|
2018-03-13 19:40:36 +02:00
|
|
|
u->source->set_state_in_main_thread = source_set_state_in_main_thread_cb;
|
2008-06-20 22:32:41 +02:00
|
|
|
u->source->userdata = u;
|
2008-06-17 18:29:15 +00:00
|
|
|
|
2009-03-30 19:13:07 +02:00
|
|
|
/* pa_source_set_latency_range(u->source, MIN_NETWORK_LATENCY_USEC, 0); */
|
2006-08-11 23:58:55 +00:00
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
|
|
|
|
|
pa_source_set_rtpoll(u->source, u->rtpoll);
|
2009-07-31 00:50:19 +02:00
|
|
|
|
|
|
|
|
u->mcalign = pa_mcalign_new(pa_frame_size(&u->source->sample_spec));
|
2004-09-29 17:38:45 +00:00
|
|
|
#endif
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2006-09-01 00:24:32 +00:00
|
|
|
u->time_event = NULL;
|
2004-09-28 23:49:54 +00:00
|
|
|
|
2009-03-31 22:16:53 +02:00
|
|
|
u->maxlength = (uint32_t) -1;
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2009-03-31 22:16:53 +02:00
|
|
|
u->tlength = u->minreq = u->prebuf = (uint32_t) -1;
|
2007-10-29 16:54:16 +00:00
|
|
|
#else
|
2009-03-31 22:16:53 +02:00
|
|
|
u->fragsize = (uint32_t) -1;
|
2007-10-29 16:54:16 +00:00
|
|
|
#endif
|
|
|
|
|
|
2010-05-03 13:28:15 +02:00
|
|
|
if (!(u->thread = pa_thread_new("module-tunnel", thread_func, u))) {
|
2007-10-29 16:54:16 +00:00
|
|
|
pa_log("Failed to create thread.");
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
pa_sink_put(u->sink);
|
|
|
|
|
#else
|
|
|
|
|
pa_source_put(u->source);
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-01-06 17:28:11 +09:00
|
|
|
pa_xfree(dn);
|
|
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
if (server)
|
|
|
|
|
pa_xfree(server);
|
|
|
|
|
|
|
|
|
|
if (server_list)
|
|
|
|
|
pa_strlist_free(server_list);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
if (xcb)
|
|
|
|
|
xcb_disconnect(xcb);
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_modargs_free(ma);
|
|
|
|
|
|
|
|
|
|
return 0;
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
fail:
|
2007-10-29 16:54:16 +00:00
|
|
|
pa__done(m);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2013-07-11 13:57:41 +02:00
|
|
|
if (server)
|
|
|
|
|
pa_xfree(server);
|
|
|
|
|
|
|
|
|
|
if (server_list)
|
|
|
|
|
pa_strlist_free(server_list);
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_X11
|
|
|
|
|
if (xcb)
|
|
|
|
|
xcb_disconnect(xcb);
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
if (ma)
|
|
|
|
|
pa_modargs_free(ma);
|
2006-08-25 22:52:59 +00:00
|
|
|
|
|
|
|
|
pa_xfree(dn);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2011-03-12 19:45:02 +01:00
|
|
|
return -1;
|
2004-09-28 22:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
void pa__done(pa_module*m) {
|
2004-09-28 22:47:48 +00:00
|
|
|
struct userdata* u;
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
pa_assert(m);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
|
|
|
|
if (!(u = m->userdata))
|
|
|
|
|
return;
|
|
|
|
|
|
2007-10-29 16:54:16 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
if (u->sink)
|
|
|
|
|
pa_sink_unlink(u->sink);
|
|
|
|
|
#else
|
|
|
|
|
if (u->source)
|
|
|
|
|
pa_source_unlink(u->source);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (u->thread) {
|
|
|
|
|
pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
|
|
|
|
|
pa_thread_free(u->thread);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_thread_mq_done(&u->thread_mq);
|
|
|
|
|
|
|
|
|
|
#ifdef TUNNEL_SINK
|
|
|
|
|
if (u->sink)
|
|
|
|
|
pa_sink_unref(u->sink);
|
|
|
|
|
#else
|
|
|
|
|
if (u->source)
|
|
|
|
|
pa_source_unref(u->source);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (u->rtpoll)
|
|
|
|
|
pa_rtpoll_free(u->rtpoll);
|
|
|
|
|
|
|
|
|
|
if (u->pstream) {
|
|
|
|
|
pa_pstream_unlink(u->pstream);
|
|
|
|
|
pa_pstream_unref(u->pstream);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (u->pdispatch)
|
|
|
|
|
pa_pdispatch_unref(u->pdispatch);
|
|
|
|
|
|
|
|
|
|
if (u->client)
|
|
|
|
|
pa_socket_client_unref(u->client);
|
2004-09-28 22:47:48 +00:00
|
|
|
|
2008-08-03 16:44:38 +02:00
|
|
|
if (u->auth_cookie)
|
|
|
|
|
pa_auth_cookie_unref(u->auth_cookie);
|
2007-10-29 16:54:16 +00:00
|
|
|
|
|
|
|
|
if (u->smoother)
|
|
|
|
|
pa_smoother_free(u->smoother);
|
|
|
|
|
|
|
|
|
|
if (u->time_event)
|
|
|
|
|
u->core->mainloop->time_free(u->time_event);
|
2007-01-04 13:43:45 +00:00
|
|
|
|
2009-07-31 00:50:19 +02:00
|
|
|
#ifndef TUNNEL_SINK
|
|
|
|
|
if (u->mcalign)
|
|
|
|
|
pa_mcalign_free(u->mcalign);
|
|
|
|
|
#endif
|
|
|
|
|
|
2004-09-29 17:38:45 +00:00
|
|
|
#ifdef TUNNEL_SINK
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_xfree(u->sink_name);
|
2004-09-29 17:38:45 +00:00
|
|
|
#else
|
|
|
|
|
pa_xfree(u->source_name);
|
|
|
|
|
#endif
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_xfree(u->server_name);
|
|
|
|
|
|
2007-10-30 01:50:22 +00:00
|
|
|
pa_xfree(u->device_description);
|
|
|
|
|
pa_xfree(u->server_fqdn);
|
|
|
|
|
pa_xfree(u->user_name);
|
|
|
|
|
|
2004-09-28 22:47:48 +00:00
|
|
|
pa_xfree(u);
|
|
|
|
|
}
|