chiark / gitweb /
stop disorder(1) permuting command line
[disorder] / clients / playrtp-oss.c
CommitLineData
cfa7dda1 1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
313acc77 4 * Portions copyright (C) 2007 Ross Younger
cfa7dda1 5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21/** @file clients/playrtp-oss.c
a9f0ad12 22 * @brief RTP player - OSS and empeg support
cfa7dda1 23 */
24
25#include <config.h>
26
a9f0ad12 27#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
cfa7dda1 28#include "types.h"
29
30#include <poll.h>
31#include <sys/ioctl.h>
a9f0ad12 32#if !EMPEG_HOST
cfa7dda1 33#include <sys/soundcard.h>
a9f0ad12 34#endif
cfa7dda1 35#include <assert.h>
36#include <pthread.h>
37#include <string.h>
38#include <fcntl.h>
39#include <unistd.h>
40#include <errno.h>
b28bddbb 41#include <arpa/inet.h>
cfa7dda1 42
43#include "mem.h"
44#include "log.h"
45#include "vector.h"
46#include "heap.h"
47#include "syscalls.h"
48#include "playrtp.h"
49
50/** @brief /dev/dsp (or whatever) */
51static int playrtp_oss_fd = -1;
52
515cd2bb 53/** @brief Audio buffer */
54static char *playrtp_oss_buffer;
55
56/** @brief Size of @ref playrtp_oss_buffer in bytes */
57static int playrtp_oss_bufsize;
58
59/** @brief Number of bytes used in @ref playrtp_oss_buffer */
60static int playrtp_oss_bufused;
61
cfa7dda1 62/** @brief Open and configure the OSS audio device */
63static void playrtp_oss_enable(void) {
64 if(playrtp_oss_fd == -1) {
a9f0ad12
RK
65#if EMPEG_HOST
66 /* empeg audio driver only knows /dev/audio, only supports the equivalent
67 * of AFMT_S16_NE, has a fixed buffer size, and does not support the
68 * SNDCTL_ ioctls. */
69 if(!device)
70 device = "/dev/audio";
71 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
72 fatal(errno, "error opening %s", device);
73 playrtp_oss_bufsize = 4608;
74#else
cfa7dda1 75 int rate = 44100, stereo = 1, format = AFMT_S16_BE;
76 if(!device) {
77 if(access("/dev/dsp", W_OK) == 0)
78 device = "/dev/dsp";
79 else if(access("/dev/audio", W_OK) == 0)
80 device = "/dev/audio";
81 else
82 fatal(0, "cannot determine default audio device");
83 }
84 if((playrtp_oss_fd = open(device, O_WRONLY)) < 0)
85 fatal(errno, "error opening %s", device);
86 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
87 fatal(errno, "ioctl SNDCTL_DSP_SETFMT");
88 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
89 fatal(errno, "ioctl SNDCTL_DSP_STEREO");
90 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
91 fatal(errno, "ioctl SNDCTL_DSP_SPEED");
92 if(rate != 44100)
93 error(0, "asking for 44100Hz, got %dHz", rate);
515cd2bb 94 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_GETBLKSIZE, &playrtp_oss_bufsize) < 0)
95 fatal(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
a9f0ad12
RK
96 info("OSS buffer size %d", playrtp_oss_bufsize);
97#endif
515cd2bb 98 playrtp_oss_buffer = xmalloc(playrtp_oss_bufsize);
99 playrtp_oss_bufused = 0;
cfa7dda1 100 nonblock(playrtp_oss_fd);
101 }
102}
103
515cd2bb 104/** @brief Flush the OSS output buffer
105 * @return 0 on success, non-0 on error
106 */
107static int playrtp_oss_flush(void) {
108 int nbyteswritten;
109
110 if(!playrtp_oss_bufused)
111 return 0; /* nothing to do */
112 /* 0 out the unused portion of the buffer */
113 memset(playrtp_oss_buffer + playrtp_oss_bufused, 0,
114 playrtp_oss_bufsize - playrtp_oss_bufused);
a9f0ad12
RK
115#if EMPEG_HOST
116 /* empeg audio driver insists on native-endian samples */
117 {
118 uint16_t *ptr,
119 *const limit = (uint16_t *)(playrtp_oss_buffer + playrtp_oss_bufused);
120
121 for(ptr = (uint16_t *)playrtp_oss_buffer; ptr < limit; ++ptr)
122 *ptr = ntohs(*ptr);
123 }
124#endif
515cd2bb 125 for(;;) {
126 nbyteswritten = write(playrtp_oss_fd,
127 playrtp_oss_buffer, playrtp_oss_bufsize);
128 if(nbyteswritten < 0) {
129 switch(errno) {
130 case EINTR:
131 break; /* try again */
132 case EAGAIN:
133 return 0; /* try later */
134 default:
135 error(errno, "error writing to %s", device);
136 return -1;
137 }
138 } else {
139 if(nbyteswritten < playrtp_oss_bufsize)
140 error(0, "%s: short write (%d/%d)",
141 device, nbyteswritten, playrtp_oss_bufsize);
b28bddbb
RK
142 if(dump_buffer) {
143 int count;
144 const int16_t *sp = (const int16_t *)playrtp_oss_buffer;
145
146 for(count = 0; count < playrtp_oss_bufsize; count += sizeof(int16_t)) {
147 dump_buffer[dump_index++] = (int16_t)ntohs(*sp++);
148 dump_index %= dump_size;
149 }
150 }
515cd2bb 151 playrtp_oss_bufused = 0;
152 return 0;
153 }
154 }
155}
156
cfa7dda1 157/** @brief Wait until the audio device can accept more data */
158static void playrtp_oss_wait(void) {
159 struct pollfd fds[1];
160 int n;
161
162 do {
163 fds[0].fd = playrtp_oss_fd;
164 fds[0].events = POLLOUT;
165 while((n = poll(fds, 1, -1)) < 0 && errno == EINTR)
166 ;
167 if(n < 0)
168 fatal(errno, "calling poll");
169 } while(!(fds[0].revents & (POLLOUT|POLLERR)));
170}
171
172/** @brief Close the OSS output device
173 * @param hard If nonzero, drop pending data
174 */
175static void playrtp_oss_disable(int hard) {
515cd2bb 176 if(hard) {
a9f0ad12
RK
177#if !EMPEG_HOST
178 /* No SNDCTL_DSP_ ioctls on empeg */
cfa7dda1 179 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
180 error(errno, "ioctl SNDCTL_DSP_RESET");
a9f0ad12 181#endif
515cd2bb 182 } else
183 playrtp_oss_flush();
cfa7dda1 184 xclose(playrtp_oss_fd);
185 playrtp_oss_fd = -1;
515cd2bb 186 free(playrtp_oss_buffer);
187 playrtp_oss_buffer = 0;
cfa7dda1 188}
189
190/** @brief Write samples to OSS output device
191 * @param data Pointer to sample data
158d0961 192 * @param samples Number of samples
cfa7dda1 193 * @return 0 on success, non-0 on error
194 */
515cd2bb 195static int playrtp_oss_write(const char *data, size_t samples) {
196 long bytes = samples * sizeof(int16_t);
197 while(bytes > 0) {
198 int n = playrtp_oss_bufsize - playrtp_oss_bufused;
199
200 if(n > bytes)
201 n = bytes;
202 memcpy(playrtp_oss_buffer + playrtp_oss_bufused, data, n);
203 bytes -= n;
204 data += n;
205 playrtp_oss_bufused += n;
206 if(playrtp_oss_bufused == playrtp_oss_bufsize)
207 if(playrtp_oss_flush())
208 return -1;
cfa7dda1 209 }
515cd2bb 210 next_timestamp += samples;
211 return 0;
cfa7dda1 212}
213
214/** @brief Play some data from packet @p p
215 *
216 * @p p is assumed to contain @ref next_timestamp.
217 */
218static int playrtp_oss_play(const struct packet *p) {
515cd2bb 219 return playrtp_oss_write
220 ((const char *)(p->samples_raw + next_timestamp - p->timestamp),
221 (p->timestamp + p->nsamples) - next_timestamp);
cfa7dda1 222}
223
224/** @brief Play some silence before packet @p p
225 *
226 * @p p is assumed to be entirely before @ref next_timestamp.
227 */
228static int playrtp_oss_infill(const struct packet *p) {
515cd2bb 229 static const char zeros[INFILL_SAMPLES * sizeof(int16_t)];
cfa7dda1 230 size_t samples_available = INFILL_SAMPLES;
231
232 if(p && samples_available > p->timestamp - next_timestamp)
233 samples_available = p->timestamp - next_timestamp;
234 return playrtp_oss_write(zeros, samples_available);
235}
236
237/** @brief OSS backend for playrtp */
238void playrtp_oss(void) {
239 int escape;
240 const struct packet *p;
241
242 pthread_mutex_lock(&lock);
243 for(;;) {
244 /* Wait for the buffer to fill up a bit */
245 playrtp_fill_buffer();
246 playrtp_oss_enable();
247 escape = 0;
248 info("Playing...");
249 /* Keep playing until the buffer empties out, we get an error */
250 while((nsamples >= minbuffer
251 || (nsamples > 0
252 && contains(pheap_first(&packets), next_timestamp)))
253 && !escape) {
254 /* Wait until we can play more */
255 pthread_mutex_unlock(&lock);
256 playrtp_oss_wait();
257 pthread_mutex_lock(&lock);
258 /* Device is ready for more data, find something to play */
259 p = playrtp_next_packet();
260 /* Play it or play some silence */
261 if(contains(p, next_timestamp))
262 escape = playrtp_oss_play(p);
263 else
264 escape = playrtp_oss_infill(p);
265 }
266 active = 0;
267 /* We stop playing for a bit until the buffer re-fills */
268 pthread_mutex_unlock(&lock);
269 playrtp_oss_disable(escape);
270 pthread_mutex_lock(&lock);
271 }
272}
273
274#endif
275
276/*
277Local Variables:
278c-basic-offset:2
279comment-column:40
280fill-column:79
281indent-tabs-mode:nil
282End:
283*/