Initial revision 6759
[qball-mpd.git] / src / audioOutputs / audioOutput_fifo.c
blob66c54dd7fa8d0b8f462f483c1a4aa1147abd0a34
1 /* the Music Player Daemon (MPD)
2 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
3 * This project's homepage is: http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "../audioOutput.h"
21 #include <stdlib.h>
23 #ifdef HAVE_FIFO
25 #include "../log.h"
26 #include "../conf.h"
27 #include "../utils.h"
28 #include "../timer.h"
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
37 #define FIFO_BUFFER_SIZE 65536 /* pipe capacity on Linux >= 2.6.11 */
39 typedef struct _FifoData {
40 char *path;
41 int input;
42 int output;
43 int created;
44 Timer *timer;
45 } FifoData;
47 static FifoData *newFifoData()
49 FifoData *ret;
51 ret = xmalloc(sizeof(FifoData));
53 ret->path = NULL;
54 ret->input = -1;
55 ret->output = -1;
56 ret->created = 0;
57 ret->timer = NULL;
59 return ret;
62 static void freeFifoData(FifoData *fd)
64 if (fd->path)
65 free(fd->path);
67 if (fd->timer)
68 timer_free(fd->timer);
70 free(fd);
73 static void removeFifo(FifoData *fd)
75 DEBUG("Removing FIFO \"%s\"\n", fd->path);
77 if (unlink(fd->path) < 0) {
78 ERROR("Could not remove FIFO \"%s\": %s\n",
79 fd->path, strerror(errno));
80 return;
83 fd->created = 0;
86 static void closeFifo(FifoData *fd)
88 struct stat st;
90 if (fd->input >= 0) {
91 close(fd->input);
92 fd->input = -1;
95 if (fd->output >= 0) {
96 close(fd->output);
97 fd->output = -1;
100 if (fd->created && (stat(fd->path, &st) == 0))
101 removeFifo(fd);
104 static int makeFifo(FifoData *fd)
106 if (mkfifo(fd->path, 0666) < 0) {
107 ERROR("Couldn't create FIFO \"%s\": %s\n",
108 fd->path, strerror(errno));
109 return -1;
112 fd->created = 1;
114 return 0;
117 static int checkFifo(FifoData *fd)
119 struct stat st;
121 if (stat(fd->path, &st) < 0) {
122 if (errno == ENOENT) {
123 /* Path doesn't exist */
124 return makeFifo(fd);
127 ERROR("Failed to stat FIFO \"%s\": %s\n",
128 fd->path, strerror(errno));
129 return -1;
132 if (!S_ISFIFO(st.st_mode)) {
133 ERROR("\"%s\" already exists, but is not a FIFO\n", fd->path);
134 return -1;
137 return 0;
140 static int openFifo(FifoData *fd)
142 if (checkFifo(fd) < 0)
143 return -1;
145 fd->input = open(fd->path, O_RDONLY|O_NONBLOCK);
146 if (fd->input < 0) {
147 ERROR("Could not open FIFO \"%s\" for reading: %s\n",
148 fd->path, strerror(errno));
149 closeFifo(fd);
150 return -1;
153 fd->output = open(fd->path, O_WRONLY|O_NONBLOCK);
154 if (fd->output < 0) {
155 ERROR("Could not open FIFO \"%s\" for writing: %s\n",
156 fd->path, strerror(errno));
157 closeFifo(fd);
158 return -1;
161 return 0;
164 static int fifo_initDriver(AudioOutput *audioOutput, ConfigParam *param)
166 FifoData *fd;
167 BlockParam *blockParam;
168 char *path;
170 blockParam = getBlockParam(param, "path");
171 if (!blockParam) {
172 FATAL("No \"path\" parameter specified for fifo output "
173 "defined at line %i\n", param->line);
176 path = parsePath(blockParam->value);
177 if (!path) {
178 FATAL("Could not parse \"path\" parameter for fifo output "
179 "at line %i\n", blockParam->line);
182 fd = newFifoData();
183 fd->path = path;
184 audioOutput->data = fd;
186 if (openFifo(fd) < 0) {
187 freeFifoData(fd);
188 return -1;
191 return 0;
194 static void fifo_finishDriver(AudioOutput *audioOutput)
196 FifoData *fd = (FifoData *)audioOutput->data;
198 closeFifo(fd);
199 freeFifoData(fd);
202 static int fifo_openDevice(AudioOutput *audioOutput)
204 FifoData *fd = (FifoData *)audioOutput->data;
206 if (fd->timer)
207 timer_free(fd->timer);
209 fd->timer = timer_new(&audioOutput->outAudioFormat);
211 audioOutput->open = 1;
213 return 0;
216 static void fifo_closeDevice(AudioOutput *audioOutput)
218 FifoData *fd = (FifoData *)audioOutput->data;
220 if (fd->timer) {
221 timer_free(fd->timer);
222 fd->timer = NULL;
225 audioOutput->open = 0;
228 static void fifo_dropBufferedAudio(AudioOutput *audioOutput)
230 FifoData *fd = (FifoData *)audioOutput->data;
231 char buf[FIFO_BUFFER_SIZE];
232 int bytes = 1;
234 timer_reset(fd->timer);
236 while (bytes > 0 && errno != EINTR)
237 bytes = read(fd->input, buf, FIFO_BUFFER_SIZE);
239 if (bytes < 0 && errno != EAGAIN) {
240 WARNING("Flush of FIFO \"%s\" failed: %s\n",
241 fd->path, strerror(errno));
245 static int fifo_playAudio(AudioOutput *audioOutput, char *playChunk, int size)
247 FifoData *fd = (FifoData *)audioOutput->data;
248 int offset = 0;
249 int bytes;
251 if (!fd->timer->started)
252 timer_start(fd->timer);
253 else
254 timer_sync(fd->timer);
256 timer_add(fd->timer, size);
258 while (size) {
259 bytes = write(fd->output, playChunk + offset, size);
260 if (bytes < 0) {
261 switch (errno) {
262 case EAGAIN:
263 /* The pipe is full, so empty it */
264 fifo_dropBufferedAudio(audioOutput);
265 continue;
266 case EINTR:
267 continue;
270 ERROR("Closing FIFO output \"%s\" due to write error: "
271 "%s\n", fd->path, strerror(errno));
272 fifo_closeDevice(audioOutput);
273 return -1;
276 size -= bytes;
277 offset += bytes;
280 return 0;
283 AudioOutputPlugin fifoPlugin = {
284 "fifo",
285 NULL, /* testDefaultDeviceFunc */
286 fifo_initDriver,
287 fifo_finishDriver,
288 fifo_openDevice,
289 fifo_playAudio,
290 fifo_dropBufferedAudio,
291 fifo_closeDevice,
292 NULL, /* sendMetadataFunc */
295 #else /* HAVE_FIFO */
297 DISABLED_AUDIO_OUTPUT_PLUGIN(fifoPlugin)
299 #endif /* !HAVE_FIFO */