mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-31 22:25:33 -04:00
module-tunnel: automatically find the PulseAudio server
Make the PulseAudio tunnel behave the same way as a client when it comes to figuring out how to connect to the current PulseAudio daemon. This can be useful if you start a second PulseAudio instance for e.g. network access.
This commit is contained in:
parent
943275d2fd
commit
23c39bb540
3 changed files with 211 additions and 10 deletions
|
|
@ -31,6 +31,10 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_X11
|
||||
#include <xcb/xcb.h>
|
||||
#endif
|
||||
|
||||
#include <pulse/rtclock.h>
|
||||
#include <pulse/timeval.h>
|
||||
#include <pulse/util.h>
|
||||
|
|
@ -54,6 +58,11 @@
|
|||
#include <pulsecore/proplist-util.h>
|
||||
#include <pulsecore/auth-cookie.h>
|
||||
#include <pulsecore/mcalign.h>
|
||||
#include <pulsecore/strlist.h>
|
||||
|
||||
#ifdef HAVE_X11
|
||||
#include <pulsecore/x11prop.h>
|
||||
#endif
|
||||
|
||||
#ifdef TUNNEL_SINK
|
||||
#include "module-tunnel-sink-symdef.h"
|
||||
|
|
@ -61,11 +70,17 @@
|
|||
#include "module-tunnel-source-symdef.h"
|
||||
#endif
|
||||
|
||||
#define ENV_DEFAULT_SINK "PULSE_SINK"
|
||||
#define ENV_DEFAULT_SOURCE "PULSE_SOURCE"
|
||||
#define ENV_DEFAULT_SERVER "PULSE_SERVER"
|
||||
#define ENV_COOKIE_FILE "PULSE_COOKIE"
|
||||
|
||||
#ifdef TUNNEL_SINK
|
||||
PA_MODULE_DESCRIPTION("Tunnel module for sinks");
|
||||
PA_MODULE_USAGE(
|
||||
"sink_name=<name for the local sink> "
|
||||
"sink_properties=<properties for the local sink> "
|
||||
"auto=<determine server/sink/cookie automatically> "
|
||||
"server=<address> "
|
||||
"sink=<remote sink name> "
|
||||
"cookie=<filename> "
|
||||
|
|
@ -78,6 +93,7 @@ PA_MODULE_DESCRIPTION("Tunnel module for sources");
|
|||
PA_MODULE_USAGE(
|
||||
"source_name=<name for the local source> "
|
||||
"source_properties=<properties for the local source> "
|
||||
"auto=<determine server/source/cookie automatically> "
|
||||
"server=<address> "
|
||||
"source=<remote source name> "
|
||||
"cookie=<filename> "
|
||||
|
|
@ -92,6 +108,7 @@ PA_MODULE_VERSION(PACKAGE_VERSION);
|
|||
PA_MODULE_LOAD_ONCE(false);
|
||||
|
||||
static const char* const valid_modargs[] = {
|
||||
"auto",
|
||||
"server",
|
||||
"cookie",
|
||||
"format",
|
||||
|
|
@ -1898,6 +1915,8 @@ static void sink_set_mute(pa_sink *sink) {
|
|||
int pa__init(pa_module*m) {
|
||||
pa_modargs *ma = NULL;
|
||||
struct userdata *u = NULL;
|
||||
char *server = NULL;
|
||||
pa_strlist *server_list = NULL;
|
||||
pa_sample_spec ss;
|
||||
pa_channel_map map;
|
||||
char *dn = NULL;
|
||||
|
|
@ -1906,6 +1925,11 @@ int pa__init(pa_module*m) {
|
|||
#else
|
||||
pa_source_new_data data;
|
||||
#endif
|
||||
bool automatic;
|
||||
#ifdef HAVE_X11
|
||||
xcb_connection_t *xcb = NULL;
|
||||
#endif
|
||||
const char *cookie_path;
|
||||
|
||||
pa_assert(m);
|
||||
|
||||
|
|
@ -1948,14 +1972,121 @@ int pa__init(pa_module*m) {
|
|||
u->rtpoll = pa_rtpoll_new();
|
||||
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
|
||||
|
||||
if (!(u->auth_cookie = pa_auth_cookie_get(u->core, pa_modargs_get_value(ma, "cookie", PA_NATIVE_COOKIE_FILE), true, PA_NATIVE_COOKIE_LENGTH)))
|
||||
if (pa_modargs_get_value_boolean(ma, "auto", &automatic) < 0) {
|
||||
pa_log("Failed to parse argument \"auto\".");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(u->server_name = pa_xstrdup(pa_modargs_get_value(ma, "server", NULL)))) {
|
||||
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 */
|
||||
if (cookie_path)
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
ss = m->core->default_sample_spec;
|
||||
map = m->core->default_channel_map;
|
||||
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
|
||||
|
|
@ -1963,11 +2094,25 @@ int pa__init(pa_module*m) {
|
|||
goto fail;
|
||||
}
|
||||
|
||||
if (!(u->client = pa_socket_client_new_string(m->core->mainloop, true, u->server_name, PA_NATIVE_DEFAULT_PORT))) {
|
||||
pa_log("Failed to connect to server '%s'", u->server_name);
|
||||
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;
|
||||
}
|
||||
|
||||
pa_socket_client_set_callback(u->client, on_connection, u);
|
||||
|
||||
#ifdef TUNNEL_SINK
|
||||
|
|
@ -1978,7 +2123,7 @@ int pa__init(pa_module*m) {
|
|||
pa_sink_new_data_init(&data);
|
||||
data.driver = __FILE__;
|
||||
data.module = m;
|
||||
data.namereg_fail = true;
|
||||
data.namereg_fail = false;
|
||||
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);
|
||||
|
|
@ -2022,7 +2167,7 @@ int pa__init(pa_module*m) {
|
|||
pa_source_new_data_init(&data);
|
||||
data.driver = __FILE__;
|
||||
data.module = m;
|
||||
data.namereg_fail = true;
|
||||
data.namereg_fail = false;
|
||||
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);
|
||||
|
|
@ -2079,6 +2224,17 @@ int pa__init(pa_module*m) {
|
|||
pa_source_put(u->source);
|
||||
#endif
|
||||
|
||||
if (server)
|
||||
pa_xfree(server);
|
||||
|
||||
if (server_list)
|
||||
pa_strlist_free(server_list);
|
||||
|
||||
#ifdef HAVE_X11
|
||||
if (xcb)
|
||||
xcb_disconnect(xcb);
|
||||
#endif
|
||||
|
||||
pa_modargs_free(ma);
|
||||
|
||||
return 0;
|
||||
|
|
@ -2086,6 +2242,17 @@ int pa__init(pa_module*m) {
|
|||
fail:
|
||||
pa__done(m);
|
||||
|
||||
if (server)
|
||||
pa_xfree(server);
|
||||
|
||||
if (server_list)
|
||||
pa_strlist_free(server_list);
|
||||
|
||||
#ifdef HAVE_X11
|
||||
if (xcb)
|
||||
xcb_disconnect(xcb);
|
||||
#endif
|
||||
|
||||
if (ma)
|
||||
pa_modargs_free(ma);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,39 @@ pa_auth_cookie* pa_auth_cookie_get(pa_core *core, const char *cn, bool create, s
|
|||
return c;
|
||||
}
|
||||
|
||||
pa_auth_cookie *pa_auth_cookie_create(pa_core *core, const void *data, size_t size) {
|
||||
pa_auth_cookie *c;
|
||||
char *t;
|
||||
|
||||
pa_assert(core);
|
||||
pa_assert(data);
|
||||
pa_assert(size > 0);
|
||||
|
||||
t = pa_xstrdup("auth-cookie");
|
||||
|
||||
if ((c = pa_shared_get(core, t))) {
|
||||
|
||||
pa_xfree(t);
|
||||
|
||||
if (c->size != size)
|
||||
return NULL;
|
||||
|
||||
return pa_auth_cookie_ref(c);
|
||||
}
|
||||
|
||||
c = pa_xmalloc(PA_ALIGN(sizeof(pa_auth_cookie)) + size);
|
||||
PA_REFCNT_INIT(c);
|
||||
c->core = core;
|
||||
c->name = t;
|
||||
c->size = size;
|
||||
|
||||
pa_assert_se(pa_shared_set(core, t, c) >= 0);
|
||||
|
||||
memcpy((uint8_t *) c + PA_ALIGN(sizeof(pa_auth_cookie)), data, size);
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
pa_auth_cookie* pa_auth_cookie_ref(pa_auth_cookie *c) {
|
||||
pa_assert(c);
|
||||
pa_assert(PA_REFCNT_VALUE(c) >= 1);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
typedef struct pa_auth_cookie pa_auth_cookie;
|
||||
|
||||
pa_auth_cookie* pa_auth_cookie_get(pa_core *c, const char *cn, bool create, size_t size);
|
||||
pa_auth_cookie* pa_auth_cookie_create(pa_core *c, const void *data, size_t size);
|
||||
pa_auth_cookie* pa_auth_cookie_ref(pa_auth_cookie *c);
|
||||
void pa_auth_cookie_unref(pa_auth_cookie *c);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue