Update to 6762
[qball-mpd.git] / src / audioOutputs / .svn / text-base / audioOutput_fifo.c.svn-base
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
4  *
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.
9  *
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
17  */
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;
58         
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;
81         }
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;
93         }
95         if (fd->output >= 0) {
96                 close(fd->output);
97                 fd->output = -1;
98         }
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;
110         }
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);
125                 }
127                 ERROR("Failed to stat FIFO \"%s\": %s\n",
128                       fd->path, strerror(errno));
129                 return -1;
130         }
132         if (!S_ISFIFO(st.st_mode)) {
133                 ERROR("\"%s\" already exists, but is not a FIFO\n", fd->path);
134                 return -1;
135         }
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;
151         }
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;
159         }
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);
174         }
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);
180         }
182         fd = newFifoData();
183         fd->path = path;
184         audioOutput->data = fd;
186         if (openFifo(fd) < 0) {
187                 freeFifoData(fd);
188                 return -1;
189         }
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;
223         }
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));
242         }
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;
268                         }
270                         ERROR("Closing FIFO output \"%s\" due to write error: "
271                               "%s\n", fd->path, strerror(errno));
272                         fifo_closeDevice(audioOutput);
273                         return -1;
274                 }
276                 size -= bytes;
277                 offset += bytes;
278         }
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 */