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