From e8c185c3c5870f21f8352c9684e6feb9c1ef37a5 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sun, 1 Mar 2009 17:10:35 +0000 Subject: [PATCH] Uniform audio version of the command backend. No attempt to rate-limit here, we just send audio data at the subprocess as fast as it can accept it. Organization: Straylight/Edgeware From: Richard Kettlewell As tested as the RTP backend i.e. not. --- lib/Makefile.am | 2 +- lib/uaudio-command.c | 145 +++++++++++++++++++++++++++++++++++++++++++ lib/uaudio-rtp.c | 2 +- lib/uaudio.c | 1 + lib/uaudio.h | 1 + lib/wstat.c | 6 +- lib/wstat.h | 4 +- 7 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 lib/uaudio-command.c diff --git a/lib/Makefile.am b/lib/Makefile.am index fe6a661..6a6596f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -85,7 +85,7 @@ libdisorder_a_SOURCES=charset.c charset.h \ uaudio.c uaudio-thread.c uaudio.h \ uaudio-oss.c uaudio-alsa.c \ uaudio-coreaudio.c \ - uaudio-rtp.c \ + uaudio-rtp.c uaudio-command.c \ url.h url.c \ user.h user.c \ unicode.h unicode.c \ diff --git a/lib/uaudio-command.c b/lib/uaudio-command.c new file mode 100644 index 0000000..12a47dc --- /dev/null +++ b/lib/uaudio-command.c @@ -0,0 +1,145 @@ +/* + * This file is part of DisOrder. + * Copyright (C) 2005, 2006, 2007, 2009 Richard Kettlewell + * Portions (C) 2007 Mark Wooding + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +/** @file lib/uaudio-command.c + * @brief Support for commmand backend */ +#include "common.h" + +#include +#include + +#include "syscalls.h" +#include "log.h" +#include "mem.h" +#include "wstat.h" +#include "uaudio.h" + +/** @brief Pipe to subprocess */ +static int command_fd; + +/** @brief Child process ID */ +static pid_t command_pid; + +static const char *const command_options[] = { + "command", + NULL +}; + +/** @brief Close pipe and wait for subprocess to terminate */ +static void command_wait(void) { + int w; + pid_t rc; + char *ws; + + close(command_fd); + while((rc = waitpid(command_pid, &w, 0) < 0 && errno == EINTR)) + ; + if(rc < 0) + fatal(errno, "waitpid"); + if(w) { + ws = wstat(w); + error(0, "command subprocess %s", ws); + xfree(ws); + } +} + +/** @brief Create subprocess */ +static void command_open(void) { + int pfd[2]; + const char *command; + + if(!(command = uaudio_get("command"))) + fatal(0, "'command' not set"); + xpipe(pfd); + command_pid = xfork(); + if(!command_pid) { + exitfn = _exit; + signal(SIGPIPE, SIG_DFL); + xdup2(pfd[0], 0); + close(pfd[0]); + close(pfd[1]); + /* TODO it would be nice to set some environment variables given the sample + * format. The original intended model is that you adapt DisOrder to the + * command you run but it'd be nice to support the opposite. */ + execl("/bin/sh", "sh", "-c", command, (char *)0); + fatal(errno, "error executing /bin/sh"); + } + close(pfd[0]); + command_pid = pfd[1]; +} + +/** @brief Send audio data to subprocess */ +static size_t command_play(void *buffer, size_t nsamples) { + const size_t bytes = nsamples * uaudio_sample_size; + int written = write(command_fd, buffer, bytes); + if(written < 0) { + switch(errno) { + case EINTR: + return 0; /* will retry */ + case EPIPE: + error(0, "audio command subprocess terminated"); + command_wait(); + command_open(); + return 0; /* will retry */ + default: + fatal(errno, "error writing to audio command subprocess"); + } + } + return written / uaudio_sample_size; +} + +static void command_start(uaudio_callback *callback, + void *userdata) { + command_open(); + uaudio_thread_start(callback, + userdata, + command_play, + uaudio_channels, + 4096 / uaudio_sample_size); +} + +static void command_stop(void) { + uaudio_thread_stop(); + command_wait(); +} + +static void command_activate(void) { + uaudio_thread_activate(); +} + +static void command_deactivate(void) { + uaudio_thread_deactivate(); +} + +const struct uaudio uaudio_command = { + .name = "command", + .options = command_options, + .start = command_start, + .stop = command_stop, + .activate = command_activate, + .deactivate = command_deactivate +}; + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/lib/uaudio-rtp.c b/lib/uaudio-rtp.c index 4ea4329..4785d62 100644 --- a/lib/uaudio-rtp.c +++ b/lib/uaudio-rtp.c @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -/** @file lib/uaudio-oss.c +/** @file lib/uaudio-rtp.c * @brief Support for RTP network play backend */ #include "common.h" diff --git a/lib/uaudio.c b/lib/uaudio.c index fc189d6..266846e 100644 --- a/lib/uaudio.c +++ b/lib/uaudio.c @@ -107,6 +107,7 @@ const struct uaudio *uaudio_apis[] = { &uaudio_oss, #endif &uaudio_rtp, + &uaudio_command, NULL, }; diff --git a/lib/uaudio.h b/lib/uaudio.h index 7968d79..a6996f8 100644 --- a/lib/uaudio.h +++ b/lib/uaudio.h @@ -117,6 +117,7 @@ extern const struct uaudio uaudio_oss; #endif extern const struct uaudio uaudio_rtp; +extern const struct uaudio uaudio_command; extern const struct uaudio *uaudio_apis[]; diff --git a/lib/wstat.c b/lib/wstat.c index dec036e..0ac2c17 100644 --- a/lib/wstat.c +++ b/lib/wstat.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2004, 2007, 2008 Richard Kettlewell + * Copyright (C) 2004, 2007-2009 Richard Kettlewell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ * @param w Exit status (e.g. from waitpid()) * @return Allocated string containing description of status */ -const char *wstat(int w) { +char *wstat(int w) { int n; char *r; @@ -48,7 +48,7 @@ const char *wstat(int w) { else n = byte_xasprintf(&r, "terminated with unknown wait status %#x", (unsigned)w); - return n >= 0 ? r : "[could not convert wait status]"; + return n >= 0 ? r : xstrdup("[could not convert wait status]"); } /* diff --git a/lib/wstat.h b/lib/wstat.h index aebe6ef..b6deb8a 100644 --- a/lib/wstat.h +++ b/lib/wstat.h @@ -1,6 +1,6 @@ /* * This file is part of DisOrder. - * Copyright (C) 2004, 2007, 2008 Richard Kettlewell + * Copyright (C) 2004, 2007-2009 Richard Kettlewell * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ #include -const char *wstat(int w); +char *wstat(int w); /* Format wait status @w@. In extremis the return value might be a * pointer to a string literal. The result should always be ASCII. */ -- [mdw]