2 * This file is part of DisOrder.
3 * Copyright (C) 2007 Richard Kettlewell
4 * Portions copyright (C) 2007 Ross Younger
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.
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.
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
21 /** @file clients/playrtp-oss.c
22 * @brief RTP player - OSS and empeg support
27 #if HAVE_SYS_SOUNDCARD_H || EMPEG_HOST
31 #include <sys/ioctl.h>
33 #include <sys/soundcard.h>
41 #include <arpa/inet.h>
50 /** @brief /dev/dsp (or whatever) */
51 static int playrtp_oss_fd
= -1;
53 /** @brief Audio buffer */
54 static char *playrtp_oss_buffer
;
56 /** @brief Size of @ref playrtp_oss_buffer in bytes */
57 static int playrtp_oss_bufsize
;
59 /** @brief Number of bytes used in @ref playrtp_oss_buffer */
60 static int playrtp_oss_bufused
;
62 /** @brief Open and configure the OSS audio device */
63 static void playrtp_oss_enable(void) {
64 if(playrtp_oss_fd
== -1) {
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
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;
75 int rate
= 44100, stereo
= 1, format
= AFMT_S16_BE
;
77 if(access("/dev/dsp", W_OK
) == 0)
79 else if(access("/dev/audio", W_OK
) == 0)
80 device
= "/dev/audio";
82 fatal(0, "cannot determine default audio device");
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");
93 error(0, "asking for 44100Hz, got %dHz", rate
);
94 if(ioctl(playrtp_oss_fd
, SNDCTL_DSP_GETBLKSIZE
, &playrtp_oss_bufsize
) < 0)
95 fatal(errno
, "ioctl SNDCTL_DSP_GETBLKSIZE");
96 info("OSS buffer size %d", playrtp_oss_bufsize
);
98 playrtp_oss_buffer
= xmalloc(playrtp_oss_bufsize
);
99 playrtp_oss_bufused
= 0;
100 nonblock(playrtp_oss_fd
);
104 /** @brief Flush the OSS output buffer
105 * @return 0 on success, non-0 on error
107 static int playrtp_oss_flush(void) {
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
);
116 /* empeg audio driver insists on native-endian samples */
119 *const limit
= (uint16_t *)(playrtp_oss_buffer
+ playrtp_oss_bufused
);
121 for(ptr
= (uint16_t *)playrtp_oss_buffer
; ptr
< limit
; ++ptr
)
126 nbyteswritten
= write(playrtp_oss_fd
,
127 playrtp_oss_buffer
, playrtp_oss_bufsize
);
128 if(nbyteswritten
< 0) {
131 break; /* try again */
133 return 0; /* try later */
135 error(errno
, "error writing to %s", device
);
139 if(nbyteswritten
< playrtp_oss_bufsize
)
140 error(0, "%s: short write (%d/%d)",
141 device
, nbyteswritten
, playrtp_oss_bufsize
);
144 const int16_t *sp
= (const int16_t *)playrtp_oss_buffer
;
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
;
151 playrtp_oss_bufused
= 0;
157 /** @brief Wait until the audio device can accept more data */
158 static void playrtp_oss_wait(void) {
159 struct pollfd fds
[1];
163 fds
[0].fd
= playrtp_oss_fd
;
164 fds
[0].events
= POLLOUT
;
165 while((n
= poll(fds
, 1, -1)) < 0 && errno
== EINTR
)
168 fatal(errno
, "calling poll");
169 } while(!(fds
[0].revents
& (POLLOUT
|POLLERR
)));
172 /** @brief Close the OSS output device
173 * @param hard If nonzero, drop pending data
175 static void playrtp_oss_disable(int hard
) {
178 /* No SNDCTL_DSP_ ioctls on empeg */
179 if(ioctl(playrtp_oss_fd
, SNDCTL_DSP_RESET
, 0) < 0)
180 error(errno
, "ioctl SNDCTL_DSP_RESET");
184 xclose(playrtp_oss_fd
);
186 free(playrtp_oss_buffer
);
187 playrtp_oss_buffer
= 0;
190 /** @brief Write samples to OSS output device
191 * @param data Pointer to sample data
192 * @param samples Number of samples
193 * @return 0 on success, non-0 on error
195 static int playrtp_oss_write(const char *data
, size_t samples
) {
196 long bytes
= samples
* sizeof(int16_t);
198 int n
= playrtp_oss_bufsize
- playrtp_oss_bufused
;
202 memcpy(playrtp_oss_buffer
+ playrtp_oss_bufused
, data
, n
);
205 playrtp_oss_bufused
+= n
;
206 if(playrtp_oss_bufused
== playrtp_oss_bufsize
)
207 if(playrtp_oss_flush())
210 next_timestamp
+= samples
;
214 /** @brief Play some data from packet @p p
216 * @p p is assumed to contain @ref next_timestamp.
218 static int playrtp_oss_play(const struct packet
*p
) {
219 return playrtp_oss_write
220 ((const char *)(p
->samples_raw
+ next_timestamp
- p
->timestamp
),
221 (p
->timestamp
+ p
->nsamples
) - next_timestamp
);
224 /** @brief Play some silence before packet @p p
226 * @p p is assumed to be entirely before @ref next_timestamp.
228 static int playrtp_oss_infill(const struct packet
*p
) {
229 static const char zeros
[INFILL_SAMPLES
* sizeof(int16_t)];
230 size_t samples_available
= INFILL_SAMPLES
;
232 if(p
&& samples_available
> p
->timestamp
- next_timestamp
)
233 samples_available
= p
->timestamp
- next_timestamp
;
234 return playrtp_oss_write(zeros
, samples_available
);
237 /** @brief OSS backend for playrtp */
238 void playrtp_oss(void) {
240 const struct packet
*p
;
242 pthread_mutex_lock(&lock
);
244 /* Wait for the buffer to fill up a bit */
245 playrtp_fill_buffer();
246 playrtp_oss_enable();
249 /* Keep playing until the buffer empties out, we get an error */
250 while((nsamples
>= minbuffer
252 && contains(pheap_first(&packets
), next_timestamp
)))
254 /* Wait until we can play more */
255 pthread_mutex_unlock(&lock
);
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
);
264 escape
= playrtp_oss_infill(p
);
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
);