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