2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
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, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 /** @file clients/playrtp-oss.c
21 * @brief RTP player - OSS and empeg support
26 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
30 #include <sys/ioctl.h>
32 #include <sys/soundcard.h>
48 /** @brief /dev/dsp (or whatever) */
49 static int playrtp_oss_fd = -1;
51 /** @brief Audio buffer */
52 static char *playrtp_oss_buffer;
54 /** @brief Size of @ref playrtp_oss_buffer in bytes */
55 static int playrtp_oss_bufsize;
57 /** @brief Number of bytes used in @ref playrtp_oss_buffer */
58 static int playrtp_oss_bufused;
60 /** @brief Open and configure the OSS audio device */
61 static void playrtp_oss_enable(void) {
62 if(playrtp_oss_fd == -1) {
64 /* empeg audio driver only knows /dev/audio, only supports the equivalent
65 * of AFMT_S16_NE, has a fixed buffer size, and does not support the
68 device = "/dev/audio";
69 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
70 fatal(errno, "error opening %s", device);
71 playrtp_oss_bufsize = 4608;
73 int rate = 44100, stereo = 1, format = AFMT_S16_BE;
75 if(access("/dev/dsp", W_OK) == 0)
77 else if(access("/dev/audio", W_OK) == 0)
78 device = "/dev/audio";
80 fatal(0, "cannot determine default audio device");
82 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
83 fatal(errno, "error opening %s", device);
84 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
85 fatal(errno, "ioctl SNDCTL_DSP_SETFMT");
86 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
87 fatal(errno, "ioctl SNDCTL_DSP_STEREO");
88 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
89 fatal(errno, "ioctl SNDCTL_DSP_SPEED");
91 error(0, "asking for 44100Hz, got %dHz", rate);
92 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_GETBLKSIZE, &playrtp_oss_bufsize) < 0)
93 fatal(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
94 info("OSS buffer size %d", playrtp_oss_bufsize);
96 playrtp_oss_buffer = xmalloc(playrtp_oss_bufsize);
97 playrtp_oss_bufused = 0;
98 nonblock(playrtp_oss_fd);
102 /** @brief Flush the OSS output buffer
103 * @return 0 on success, non-0 on error
105 static int playrtp_oss_flush(void) {
108 if(!playrtp_oss_bufused)
109 return 0; /* nothing to do */
110 /* 0 out the unused portion of the buffer */
111 memset(playrtp_oss_buffer + playrtp_oss_bufused, 0,
112 playrtp_oss_bufsize - playrtp_oss_bufused);
114 /* empeg audio driver insists on native-endian samples */
117 *const limit = (uint16_t *)(playrtp_oss_buffer + playrtp_oss_bufused);
119 for(ptr = (uint16_t *)playrtp_oss_buffer; ptr < limit; ++ptr)
124 nbyteswritten = write(playrtp_oss_fd,
125 playrtp_oss_buffer, playrtp_oss_bufsize);
126 if(nbyteswritten < 0) {
129 break; /* try again */
131 return 0; /* try later */
133 error(errno, "error writing to %s", device);
137 if(nbyteswritten < playrtp_oss_bufsize)
138 error(0, "%s: short write (%d/%d)",
139 device, nbyteswritten, playrtp_oss_bufsize);
140 playrtp_oss_bufused = 0;
146 /** @brief Wait until the audio device can accept more data */
147 static void playrtp_oss_wait(void) {
148 struct pollfd fds[1];
152 fds[0].fd = playrtp_oss_fd;
153 fds[0].events = POLLOUT;
154 while((n = poll(fds, 1, -1)) < 0 && errno == EINTR)
157 fatal(errno, "calling poll");
158 } while(!(fds[0].revents & (POLLOUT|POLLERR)));
161 /** @brief Close the OSS output device
162 * @param hard If nonzero, drop pending data
164 static void playrtp_oss_disable(int hard) {
167 /* No SNDCTL_DSP_ ioctls on empeg */
168 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
169 error(errno, "ioctl SNDCTL_DSP_RESET");
173 xclose(playrtp_oss_fd);
175 free(playrtp_oss_buffer);
176 playrtp_oss_buffer = 0;
179 /** @brief Write samples to OSS output device
180 * @param data Pointer to sample data
181 * @param nsamples Number of samples
182 * @return 0 on success, non-0 on error
184 static int playrtp_oss_write(const char *data, size_t samples) {
185 long bytes = samples * sizeof(int16_t);
187 int n = playrtp_oss_bufsize - playrtp_oss_bufused;
191 memcpy(playrtp_oss_buffer + playrtp_oss_bufused, data, n);
194 playrtp_oss_bufused += n;
195 if(playrtp_oss_bufused == playrtp_oss_bufsize)
196 if(playrtp_oss_flush())
199 next_timestamp += samples;
203 /** @brief Play some data from packet @p p
205 * @p p is assumed to contain @ref next_timestamp.
207 static int playrtp_oss_play(const struct packet *p) {
208 return playrtp_oss_write
209 ((const char *)(p->samples_raw + next_timestamp - p->timestamp),
210 (p->timestamp + p->nsamples) - next_timestamp);
213 /** @brief Play some silence before packet @p p
215 * @p p is assumed to be entirely before @ref next_timestamp.
217 static int playrtp_oss_infill(const struct packet *p) {
218 static const char zeros[INFILL_SAMPLES * sizeof(int16_t)];
219 size_t samples_available = INFILL_SAMPLES;
221 if(p && samples_available > p->timestamp - next_timestamp)
222 samples_available = p->timestamp - next_timestamp;
223 return playrtp_oss_write(zeros, samples_available);
226 /** @brief OSS backend for playrtp */
227 void playrtp_oss(void) {
229 const struct packet *p;
231 pthread_mutex_lock(&lock);
233 /* Wait for the buffer to fill up a bit */
234 playrtp_fill_buffer();
235 playrtp_oss_enable();
238 /* Keep playing until the buffer empties out, we get an error */
239 while((nsamples >= minbuffer
241 && contains(pheap_first(&packets), next_timestamp)))
243 /* Wait until we can play more */
244 pthread_mutex_unlock(&lock);
246 pthread_mutex_lock(&lock);
247 /* Device is ready for more data, find something to play */
248 p = playrtp_next_packet();
249 /* Play it or play some silence */
250 if(contains(p, next_timestamp))
251 escape = playrtp_oss_play(p);
253 escape = playrtp_oss_infill(p);
256 /* We stop playing for a bit until the buffer re-fills */
257 pthread_mutex_unlock(&lock);
258 playrtp_oss_disable(escape);
259 pthread_mutex_lock(&lock);