chiark / gitweb /
align with disorder.unicode a bit
[disorder] / clients / playrtp-oss.c
CommitLineData
cfa7dda1 1/*
2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
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, 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.
14 *
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
18 * USA
19 */
20/** @file clients/playrtp-oss.c
a9f0ad12 21 * @brief RTP player - OSS and empeg support
cfa7dda1 22 */
23
24#include <config.h>
25
a9f0ad12 26#if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
cfa7dda1 27#include "types.h"
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 <assert.h>
35#include <pthread.h>
36#include <string.h>
37#include <fcntl.h>
38#include <unistd.h>
39#include <errno.h>
40
41#include "mem.h"
42#include "log.h"
43#include "vector.h"
44#include "heap.h"
45#include "syscalls.h"
46#include "playrtp.h"
47
48/** @brief /dev/dsp (or whatever) */
49static int playrtp_oss_fd = -1;
50
515cd2bb 51/** @brief Audio buffer */
52static char *playrtp_oss_buffer;
53
54/** @brief Size of @ref playrtp_oss_buffer in bytes */
55static int playrtp_oss_bufsize;
56
57/** @brief Number of bytes used in @ref playrtp_oss_buffer */
58static int playrtp_oss_bufused;
59
cfa7dda1 60/** @brief Open and configure the OSS audio device */
61static void playrtp_oss_enable(void) {
62 if(playrtp_oss_fd == -1) {
a9f0ad12
RK
63#if EMPEG_HOST
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
66 * SNDCTL_ ioctls. */
67 if(!device)
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;
72#else
cfa7dda1 73 int rate = 44100, stereo = 1, format = AFMT_S16_BE;
74 if(!device) {
75 if(access("/dev/dsp", W_OK) == 0)
76 device = "/dev/dsp";
77 else if(access("/dev/audio", W_OK) == 0)
78 device = "/dev/audio";
79 else
80 fatal(0, "cannot determine default audio device");
81 }
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");
90 if(rate != 44100)
91 error(0, "asking for 44100Hz, got %dHz", rate);
515cd2bb 92 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_GETBLKSIZE, &playrtp_oss_bufsize) < 0)
93 fatal(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
a9f0ad12
RK
94 info("OSS buffer size %d", playrtp_oss_bufsize);
95#endif
515cd2bb 96 playrtp_oss_buffer = xmalloc(playrtp_oss_bufsize);
97 playrtp_oss_bufused = 0;
cfa7dda1 98 nonblock(playrtp_oss_fd);
99 }
100}
101
515cd2bb 102/** @brief Flush the OSS output buffer
103 * @return 0 on success, non-0 on error
104 */
105static int playrtp_oss_flush(void) {
106 int nbyteswritten;
107
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);
a9f0ad12
RK
113#if EMPEG_HOST
114 /* empeg audio driver insists on native-endian samples */
115 {
116 uint16_t *ptr,
117 *const limit = (uint16_t *)(playrtp_oss_buffer + playrtp_oss_bufused);
118
119 for(ptr = (uint16_t *)playrtp_oss_buffer; ptr < limit; ++ptr)
120 *ptr = ntohs(*ptr);
121 }
122#endif
515cd2bb 123 for(;;) {
124 nbyteswritten = write(playrtp_oss_fd,
125 playrtp_oss_buffer, playrtp_oss_bufsize);
126 if(nbyteswritten < 0) {
127 switch(errno) {
128 case EINTR:
129 break; /* try again */
130 case EAGAIN:
131 return 0; /* try later */
132 default:
133 error(errno, "error writing to %s", device);
134 return -1;
135 }
136 } else {
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;
141 return 0;
142 }
143 }
144}
145
cfa7dda1 146/** @brief Wait until the audio device can accept more data */
147static void playrtp_oss_wait(void) {
148 struct pollfd fds[1];
149 int n;
150
151 do {
152 fds[0].fd = playrtp_oss_fd;
153 fds[0].events = POLLOUT;
154 while((n = poll(fds, 1, -1)) < 0 && errno == EINTR)
155 ;
156 if(n < 0)
157 fatal(errno, "calling poll");
158 } while(!(fds[0].revents & (POLLOUT|POLLERR)));
159}
160
161/** @brief Close the OSS output device
162 * @param hard If nonzero, drop pending data
163 */
164static void playrtp_oss_disable(int hard) {
515cd2bb 165 if(hard) {
a9f0ad12
RK
166#if !EMPEG_HOST
167 /* No SNDCTL_DSP_ ioctls on empeg */
cfa7dda1 168 if(ioctl(playrtp_oss_fd, SNDCTL_DSP_RESET, 0) < 0)
169 error(errno, "ioctl SNDCTL_DSP_RESET");
a9f0ad12 170#endif
515cd2bb 171 } else
172 playrtp_oss_flush();
cfa7dda1 173 xclose(playrtp_oss_fd);
174 playrtp_oss_fd = -1;
515cd2bb 175 free(playrtp_oss_buffer);
176 playrtp_oss_buffer = 0;
cfa7dda1 177}
178
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
183 */
515cd2bb 184static int playrtp_oss_write(const char *data, size_t samples) {
185 long bytes = samples * sizeof(int16_t);
186 while(bytes > 0) {
187 int n = playrtp_oss_bufsize - playrtp_oss_bufused;
188
189 if(n > bytes)
190 n = bytes;
191 memcpy(playrtp_oss_buffer + playrtp_oss_bufused, data, n);
192 bytes -= n;
193 data += n;
194 playrtp_oss_bufused += n;
195 if(playrtp_oss_bufused == playrtp_oss_bufsize)
196 if(playrtp_oss_flush())
197 return -1;
cfa7dda1 198 }
515cd2bb 199 next_timestamp += samples;
200 return 0;
cfa7dda1 201}
202
203/** @brief Play some data from packet @p p
204 *
205 * @p p is assumed to contain @ref next_timestamp.
206 */
207static int playrtp_oss_play(const struct packet *p) {
515cd2bb 208 return playrtp_oss_write
209 ((const char *)(p->samples_raw + next_timestamp - p->timestamp),
210 (p->timestamp + p->nsamples) - next_timestamp);
cfa7dda1 211}
212
213/** @brief Play some silence before packet @p p
214 *
215 * @p p is assumed to be entirely before @ref next_timestamp.
216 */
217static int playrtp_oss_infill(const struct packet *p) {
515cd2bb 218 static const char zeros[INFILL_SAMPLES * sizeof(int16_t)];
cfa7dda1 219 size_t samples_available = INFILL_SAMPLES;
220
221 if(p && samples_available > p->timestamp - next_timestamp)
222 samples_available = p->timestamp - next_timestamp;
223 return playrtp_oss_write(zeros, samples_available);
224}
225
226/** @brief OSS backend for playrtp */
227void playrtp_oss(void) {
228 int escape;
229 const struct packet *p;
230
231 pthread_mutex_lock(&lock);
232 for(;;) {
233 /* Wait for the buffer to fill up a bit */
234 playrtp_fill_buffer();
235 playrtp_oss_enable();
236 escape = 0;
237 info("Playing...");
238 /* Keep playing until the buffer empties out, we get an error */
239 while((nsamples >= minbuffer
240 || (nsamples > 0
241 && contains(pheap_first(&packets), next_timestamp)))
242 && !escape) {
243 /* Wait until we can play more */
244 pthread_mutex_unlock(&lock);
245 playrtp_oss_wait();
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);
252 else
253 escape = playrtp_oss_infill(p);
254 }
255 active = 0;
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);
260 }
261}
262
263#endif
264
265/*
266Local Variables:
267c-basic-offset:2
268comment-column:40
269fill-column:79
270indent-tabs-mode:nil
271End:
272*/