/*-*- linux-c -*-*/ /* * ALSA <-> PulseAudio plugins * * Copyright (c) 2006 by Pierre Ossman * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "pulse.h" int pulse_check_connection(snd_pulse_t * p) { pa_context_state_t state; assert(p); if (!p->context || !p->mainloop) return -EBADFD; state = pa_context_get_state(p->context); if (!PA_CONTEXT_IS_GOOD(state)) return -EIO; return 0; } void pulse_context_success_cb(pa_context * c, int success, void *userdata) { snd_pulse_t *p = userdata; assert(c); assert(p); pa_threaded_mainloop_signal(p->mainloop, 0); } int pulse_wait_operation(snd_pulse_t * p, pa_operation * o) { assert(p); assert(o); for (;;) { int err; err = pulse_check_connection(p); if (err < 0) return err; if (pa_operation_get_state(o) != PA_OPERATION_RUNNING) break; pa_threaded_mainloop_wait(p->mainloop); } return 0; } static void context_state_cb(pa_context * c, void *userdata) { pa_context_state_t state; snd_pulse_t *p = userdata; assert(c); state = pa_context_get_state(c); /* When we get disconnected, tell the process */ if (!PA_CONTEXT_IS_GOOD(state)) pulse_poll_activate(p); switch (state) { case PA_CONTEXT_READY: case PA_CONTEXT_TERMINATED: case PA_CONTEXT_FAILED: pa_threaded_mainloop_signal(p->mainloop, 0); break; case PA_CONTEXT_UNCONNECTED: case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; } } static int make_nonblock(int fd) { int fl; if ((fl = fcntl(fd, F_GETFL)) < 0) return fl; if (fl & O_NONBLOCK) return 0; return fcntl(fd, F_SETFL, fl | O_NONBLOCK); } static int make_close_on_exec(int fd) { return fcntl(fd, F_SETFD, FD_CLOEXEC); } snd_pulse_t *pulse_new(void) { snd_pulse_t *p; int fd[2] = { -1, -1 }; char proc[PATH_MAX], buf[PATH_MAX + 20]; p = calloc(1, sizeof(snd_pulse_t)); if (!p) return NULL; if (pipe(fd)) { free(p); return NULL; } p->main_fd = fd[0]; p->thread_fd = fd[1]; make_nonblock(p->main_fd); make_close_on_exec(p->main_fd); make_nonblock(p->thread_fd); make_close_on_exec(p->thread_fd); p->mainloop = pa_threaded_mainloop_new(); if (!p->mainloop) goto fail; if (pa_get_binary_name(proc, sizeof(proc))) snprintf(buf, sizeof(buf), "ALSA plug-in [%s]", pa_path_get_filename(proc)); else snprintf(buf, sizeof(buf), "ALSA plug-in"); buf[sizeof(buf)-1] = 0; p->context = pa_context_new(pa_threaded_mainloop_get_api(p->mainloop), buf); if (!p->context) goto fail; pa_context_set_state_callback(p->context, context_state_cb, p); if (pa_threaded_mainloop_start(p->mainloop) < 0) goto fail; return p; fail: pulse_free(p); return NULL; } void pulse_free(snd_pulse_t * p) { if (p->mainloop) pa_threaded_mainloop_stop(p->mainloop); if (p->context) { pa_context_disconnect(p->context); pa_context_unref(p->context); } if (p->mainloop) pa_threaded_mainloop_free(p->mainloop); if (p->thread_fd >= 0) close(p->thread_fd); if (p->main_fd >= 0) close(p->main_fd); free(p); } int pulse_connect(snd_pulse_t * p, const char *server, int can_fallback) { int err; pa_context_flags_t flags; pa_context_state_t state; assert(p); if (can_fallback) flags = PA_CONTEXT_NOAUTOSPAWN; else flags = 0; if (!p->context || !p->mainloop) return -EBADFD; state = pa_context_get_state(p->context); if (state != PA_CONTEXT_UNCONNECTED) return -EBADFD; pa_threaded_mainloop_lock(p->mainloop); err = pa_context_connect(p->context, server, flags, NULL); if (err < 0) goto error; for (;;) { pa_context_state_t state = pa_context_get_state(p->context); if (!PA_CONTEXT_IS_GOOD(state)) goto error; if (state == PA_CONTEXT_READY) break; pa_threaded_mainloop_wait(p->mainloop); } pa_threaded_mainloop_unlock(p->mainloop); return 0; error: if (!can_fallback) SNDERR("PulseAudio: Unable to connect: %s\n", pa_strerror(pa_context_errno(p->context))); pa_threaded_mainloop_unlock(p->mainloop); return -ECONNREFUSED; } void pulse_poll_activate(snd_pulse_t * p) { static const char x = 'x'; assert(p); write(p->thread_fd, &x, 1); } void pulse_poll_deactivate(snd_pulse_t * p) { char buf[10]; assert(p); /* Drain the pipe */ while (read(p->main_fd, buf, sizeof(buf)) > 0); }