Skip to content

Commit

Permalink
pulseaudio: support reconnecting
Browse files Browse the repository at this point in the history
Recreate vchan servers (libvchan_server_init()) on disconnection.

This fixes audio in the case of Xorg crash (see
QubesOS/qubes-issues#5273), and any kind of GUI restart.
  • Loading branch information
pwmarcz committed Mar 26, 2020
1 parent b296e45 commit fe0b701
Showing 1 changed file with 45 additions and 22 deletions.
67 changes: 45 additions & 22 deletions pulse/module-vchan-sink.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct userdata {
pa_sink *sink;
pa_source *source;

int domid;
libvchan_t *play_ctrl;
libvchan_t *rec_ctrl;

Expand Down Expand Up @@ -145,6 +146,8 @@ static const char *const valid_modargs[] = {
NULL
};

static int do_conn(struct userdata *u);

#if PA_CHECK_VERSION(12,0,0)
static int sink_set_state_in_io_thread_cb(pa_sink *s, pa_sink_state_t new_state,
pa_suspend_cause_t suspend_cause __attribute__((unused))) {
Expand Down Expand Up @@ -422,6 +425,26 @@ static void thread_func(void *userdata)
struct pollfd *rec_pollfd;
int ret;

if (!libvchan_is_open(u->play_ctrl) || !libvchan_is_open(u->rec_ctrl)) {
pa_log("vchan disconnected, restarting server");

libvchan_close(u->play_ctrl);
libvchan_close(u->rec_ctrl);
pa_rtpoll_item_free(u->play_rtpoll_item);
pa_rtpoll_item_free(u->rec_rtpoll_item);

u->play_ctrl = NULL;
u->rec_ctrl = NULL;
u->play_rtpoll_item = NULL;
u->rec_rtpoll_item = NULL;

if (do_conn(u) < 0) {
pa_log("failed to restart vchan server");
goto fail;
}
pa_log("vchan server restarted");
}

play_pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL);
rec_pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL);

Expand Down Expand Up @@ -488,23 +511,35 @@ static void thread_func(void *userdata)
pa_log_debug("Thread shutting down");
}

static int do_conn(struct userdata *u, int domid)
static int do_conn(struct userdata *u)
{
int fd;
u->play_ctrl = libvchan_server_init(domid, QUBES_PA_SINK_VCHAN_PORT, 128, 2048);
struct pollfd *pollfd;

u->play_ctrl = libvchan_server_init(u->domid, QUBES_PA_SINK_VCHAN_PORT, 128, 2048);
if (!u->play_ctrl) {
pa_log("libvchan_server_init play failed\n");
return -1;
}
u->rec_ctrl = libvchan_server_init(domid, QUBES_PA_SOURCE_VCHAN_PORT, 2048, 128);
u->rec_ctrl = libvchan_server_init(u->domid, QUBES_PA_SOURCE_VCHAN_PORT, 2048, 128);
if (!u->rec_ctrl) {
pa_log("libvchan_server_init rec failed\n");
return -1;
}
fd = libvchan_fd_for_select(u->play_ctrl);
pa_log("play libvchan_fd_for_select=%d, ctrl=%p\n", fd, u->play_ctrl);
fd = libvchan_fd_for_select(u->rec_ctrl);
pa_log("rec libvchan_fd_for_select=%d, ctrl=%p\n", fd, u->rec_ctrl);

u->play_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL);
pollfd->fd = libvchan_fd_for_select(u->play_ctrl);
pollfd->events = POLLIN;
pollfd->revents = 0;
pa_log("play libvchan_fd_for_select=%d, ctrl=%p\n", pollfd->fd, u->play_ctrl);

u->rec_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL);
pollfd->fd = libvchan_fd_for_select(u->rec_ctrl);
pollfd->events = POLLIN;
pollfd->revents = 0;
pa_log("rec libvchan_fd_for_select=%d, ctrl=%p\n", pollfd->fd, u->rec_ctrl);

return 0;
}

Expand Down Expand Up @@ -622,7 +657,6 @@ int pa__init(pa_module * m)
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma;
struct pollfd *pollfd;
pa_sink_new_data data_sink;
pa_source_new_data data_source;
int domid = DEFAULT_DOMID;
Expand Down Expand Up @@ -675,7 +709,8 @@ int pa__init(pa_module * m)

pa_log("using domid: %d", domid);
pa_modargs_get_value_s32(ma, "domid", &domid);
if ((do_conn(u, domid)) < 0) {
u->domid = domid;
if ((do_conn(u)) < 0) {

pa_log("get_early_allocated_vchan: %s",
pa_cstrerror(errno));
Expand Down Expand Up @@ -734,12 +769,6 @@ int pa__init(pa_module * m)
(VCHAN_BUF,
&u->sink->sample_spec));

u->play_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL);
pollfd->fd = libvchan_fd_for_select(u->play_ctrl);
pollfd->events = POLLIN;
pollfd->revents = 0;

/* SOURCE preparation */
pa_source_new_data_init(&data_source);
data_source.driver = __FILE__;
Expand Down Expand Up @@ -777,12 +806,6 @@ int pa__init(pa_module * m)
pa_source_set_rtpoll(u->source, u->rtpoll);
pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(PIPE_BUF, &u->source->sample_spec));

u->rec_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL);
pollfd->fd = libvchan_fd_for_select(u->rec_ctrl);
pollfd->events = POLLIN;
pollfd->revents = 0;

#if PA_CHECK_VERSION(0,9,22)
if (!(u->thread = pa_thread_new("vchan-sink", thread_func, u))) {
#else
Expand Down

0 comments on commit fe0b701

Please sign in to comment.