Mercurial > mplayer.hg
comparison libao2/ao_nas.c @ 3276:e279cc05f189
-ao NAS support by Tobias Diedrich <ranma@gmx.at>
author | arpi |
---|---|
date | Mon, 03 Dec 2001 01:13:48 +0000 |
parents | |
children | 8818c12743a8 |
comparison
equal
deleted
inserted
replaced
3275:38344371432f | 3276:e279cc05f189 |
---|---|
1 #include <stdio.h> | |
2 #include <stdlib.h> | |
3 #include <pthread.h> | |
4 #include <audio/audiolib.h> | |
5 | |
6 #include "audio_out.h" | |
7 #include "audio_out_internal.h" | |
8 #include "afmt.h" | |
9 | |
10 #define FRAG_SIZE 4096 | |
11 #define FRAG_COUNT 8 | |
12 #define BUFFER_SIZE FRAG_SIZE * FRAG_COUNT | |
13 | |
14 #define NAS_DEBUG 0 | |
15 | |
16 #if NAS_DEBUG == 1 | |
17 #define DPRINTF(format, args...) fprintf(stderr, format, ## args); \ | |
18 fflush(stderr) | |
19 #else | |
20 #define DPRINTF(format, args...) | |
21 #endif | |
22 | |
23 static ao_info_t info = | |
24 { | |
25 "NAS audio output", | |
26 "nas", | |
27 "Tobias Diedrich", | |
28 "" | |
29 }; | |
30 | |
31 static AuServer* aud; | |
32 static AuFlowID flow; | |
33 static AuDeviceID dev; | |
34 | |
35 static void *client_buffer; | |
36 static int client_buffer_size = BUFFER_SIZE; | |
37 static int client_buffer_used; | |
38 static int server_buffer_size = BUFFER_SIZE; | |
39 static int server_buffer_used; | |
40 static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER; | |
41 | |
42 pthread_t event_thread; | |
43 static int stop_thread; | |
44 | |
45 LIBAO_EXTERN(nas) | |
46 | |
47 static void wait_for_event() | |
48 { | |
49 AuEvent ev; | |
50 | |
51 AuNextEvent(aud, AuTrue, &ev); | |
52 AuDispatchEvent(aud, &ev); | |
53 } | |
54 | |
55 static int readBuffer(int num) | |
56 { | |
57 pthread_mutex_lock(&buffer_mutex); | |
58 DPRINTF("readBuffer(): num=%d client=%d/%d server=%d/%d\n", | |
59 num, | |
60 client_buffer_used, client_buffer_size, | |
61 server_buffer_used, server_buffer_size); | |
62 | |
63 if (client_buffer_used == 0) { | |
64 DPRINTF("buffer is empty, nothing read.\n"); | |
65 pthread_mutex_unlock(&buffer_mutex); | |
66 return 0; | |
67 } | |
68 if (client_buffer_used < num) | |
69 num = client_buffer_used; | |
70 | |
71 AuWriteElement(aud, flow, 0, num, client_buffer, AuFalse, NULL); | |
72 client_buffer_used -= num; | |
73 server_buffer_used += num; | |
74 memmove(client_buffer, client_buffer + num, client_buffer_used); | |
75 pthread_mutex_unlock(&buffer_mutex); | |
76 | |
77 return num; | |
78 } | |
79 | |
80 static void writeBuffer(void *data, int len) | |
81 { | |
82 pthread_mutex_lock(&buffer_mutex); | |
83 DPRINTF("writeBuffer(): len=%d client=%d/%d server=%d/%d\n", | |
84 len, client_buffer_used, client_buffer_size, | |
85 server_buffer_used, server_buffer_size); | |
86 | |
87 memcpy(client_buffer + client_buffer_used, data, len); | |
88 client_buffer_used += len; | |
89 | |
90 pthread_mutex_unlock(&buffer_mutex); | |
91 if (server_buffer_used < server_buffer_size) | |
92 readBuffer(server_buffer_size - server_buffer_used); | |
93 } | |
94 | |
95 | |
96 static void *event_thread_start(void *data) | |
97 { | |
98 while (!stop_thread) { | |
99 wait_for_event(); | |
100 } | |
101 } | |
102 | |
103 static AuBool event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd) | |
104 { | |
105 switch (ev->type) { | |
106 case AuEventTypeElementNotify: { | |
107 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev; | |
108 DPRINTF("event_handler(): kind %d state %d->%d reason %d numbytes %d\n", | |
109 event->kind, | |
110 event->prev_state, | |
111 event->cur_state, | |
112 event->reason, | |
113 event->num_bytes); | |
114 | |
115 switch (event->kind) { | |
116 case AuElementNotifyKindLowWater: | |
117 server_buffer_used -= event->num_bytes; | |
118 readBuffer(event->num_bytes); | |
119 break; | |
120 case AuElementNotifyKindState: | |
121 if ((event->cur_state == AuStatePause) && | |
122 (event->reason != AuReasonUser)) { | |
123 // buffer underrun -> refill buffer | |
124 server_buffer_used = 0; | |
125 readBuffer(server_buffer_size - server_buffer_used); | |
126 } | |
127 } | |
128 } | |
129 } | |
130 return AuTrue; | |
131 } | |
132 | |
133 static AuBool error_handler(AuServer* aud, AuErrorEvent* ev) | |
134 { | |
135 char s[100]; | |
136 AuGetErrorText(aud, ev->error_code, s, 100); | |
137 fprintf(stderr,"libaudiooss: error [%s]\n" | |
138 "error_code: %d\n" | |
139 "request_code: %d\n" | |
140 "minor_code: %d\n", | |
141 s, | |
142 ev->error_code, | |
143 ev->request_code, | |
144 ev->minor_code); | |
145 fflush(stderr); | |
146 | |
147 return AuTrue; | |
148 } | |
149 | |
150 static AuDeviceID find_device(int nch) | |
151 { | |
152 int i; | |
153 for (i = 0; i < AuServerNumDevices(aud); i++) { | |
154 AuDeviceAttributes *dev = AuServerDevice(aud, i); | |
155 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) && | |
156 AuDeviceNumTracks(dev) == nch) { | |
157 return AuDeviceIdentifier(dev); | |
158 } | |
159 } | |
160 return AuNone; | |
161 } | |
162 | |
163 static unsigned char aformat_to_auformat(unsigned int format) | |
164 { | |
165 switch (format) { | |
166 case AFMT_U8: return AuFormatLinearUnsigned8; | |
167 case AFMT_S8: return AuFormatLinearSigned8; | |
168 case AFMT_U16_LE: return AuFormatLinearUnsigned16LSB; | |
169 case AFMT_U16_BE: return AuFormatLinearUnsigned16MSB; | |
170 case AFMT_S16_LE: return AuFormatLinearSigned16LSB; | |
171 case AFMT_S16_BE: return AuFormatLinearSigned16MSB; | |
172 case AFMT_MU_LAW: return AuFormatULAW8; | |
173 default: return 0; | |
174 } | |
175 } | |
176 | |
177 // to set/get/query special features/parameters | |
178 static int control(int cmd,int arg){ | |
179 return -1; | |
180 } | |
181 | |
182 // open & setup audio device | |
183 // return: 1=success 0=fail | |
184 static int init(int rate,int channels,int format,int flags) | |
185 { | |
186 AuElement elms[3]; | |
187 AuStatus as; | |
188 unsigned char auformat = aformat_to_auformat(format); | |
189 int bytes_per_sample; | |
190 char *server; | |
191 | |
192 printf("ao2: %d Hz %d chans %s\n",rate,channels, | |
193 audio_out_format_name(format)); | |
194 | |
195 if (!auformat) { | |
196 printf("Unsupported format -> nosound\n"); | |
197 return 0; | |
198 } | |
199 | |
200 client_buffer = malloc(BUFFER_SIZE); | |
201 bytes_per_sample = channels * AuSizeofFormat(auformat); | |
202 | |
203 ao_data.samplerate = rate; | |
204 ao_data.channels = channels; | |
205 ao_data.buffersize = BUFFER_SIZE * 2; | |
206 ao_data.outburst = FRAG_SIZE; | |
207 ao_data.bps = rate * bytes_per_sample; | |
208 | |
209 if (!bytes_per_sample) { | |
210 printf("Zero bytes per sample -> nosound\n"); | |
211 return 0; | |
212 } | |
213 | |
214 if (!(server = getenv("AUDIOSERVER"))) | |
215 server = getenv("DISPLAY"); | |
216 | |
217 if (!server) // default to tcp/localhost:8000 | |
218 server = "tcp/localhost:8000"; | |
219 | |
220 printf("Using audioserver %s\n", server); | |
221 | |
222 aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL); | |
223 if (!aud){ | |
224 printf("Can't open nas audio server -> nosound\n"); | |
225 return 0; | |
226 } | |
227 | |
228 dev = find_device(channels); | |
229 if ((dev == AuNone) || (!(flow = AuCreateFlow(aud, NULL)))) { | |
230 printf("Can't find a device serving that many channels -> nosound\n"); | |
231 AuCloseServer(aud); | |
232 aud = 0; | |
233 return 0; | |
234 } | |
235 | |
236 AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue, | |
237 BUFFER_SIZE / bytes_per_sample, | |
238 (BUFFER_SIZE - FRAG_SIZE) / bytes_per_sample, | |
239 0, NULL); | |
240 AuMakeElementExportDevice(elms+1, 0, dev, rate, | |
241 AuUnlimitedSamples, 0, NULL); | |
242 AuSetElements(aud, flow, AuTrue, 2, elms, &as); | |
243 if (as != AuSuccess) | |
244 printf("AuSetElements returned status %d!\n", as); | |
245 AuRegisterEventHandler(aud, AuEventHandlerIDMask | | |
246 AuEventHandlerTypeMask, | |
247 AuEventTypeElementNotify, flow, | |
248 event_handler, (AuPointer) NULL); | |
249 AuSetErrorHandler(aud, error_handler); | |
250 AuStartFlow(aud, flow, &as); | |
251 if (as != AuSuccess) | |
252 printf("AuSetElements returned status %d!\n", as); | |
253 | |
254 /* | |
255 * Wait for first buffer underrun event | |
256 * | |
257 * For some weird reason we get a buffer underrun event if we | |
258 * don't fill the server buffer fast enough after staring the | |
259 * flow. So we just wait for it to happen to be in a sane state. | |
260 */ | |
261 wait_for_event(); | |
262 | |
263 pthread_create(&event_thread, NULL, &event_thread_start, NULL); | |
264 | |
265 return 1; | |
266 } | |
267 | |
268 // close audio device | |
269 static void uninit(){ | |
270 stop_thread = 1; | |
271 pthread_join(event_thread, NULL); | |
272 AuStopFlow(aud, flow, NULL); | |
273 AuCloseServer(aud); | |
274 aud = 0; | |
275 free(client_buffer); | |
276 } | |
277 | |
278 // stop playing and empty buffers (for seeking/pause) | |
279 static void reset(){ | |
280 pthread_mutex_lock(&buffer_mutex); | |
281 client_buffer_used = 0; | |
282 pthread_mutex_unlock(&buffer_mutex); | |
283 while (server_buffer_used > 0) { | |
284 usleep(1000); | |
285 // DPRINTF("used=%d\n", server_buffer_used); | |
286 } | |
287 } | |
288 | |
289 // stop playing, keep buffers (for pause) | |
290 static void audio_pause() | |
291 { | |
292 // for now, just call reset(); | |
293 reset(); | |
294 } | |
295 | |
296 // resume playing, after audio_pause() | |
297 static void audio_resume() | |
298 { | |
299 } | |
300 | |
301 // return: how many bytes can be played without blocking | |
302 static int get_space() | |
303 { | |
304 int result; | |
305 | |
306 pthread_mutex_lock(&buffer_mutex); | |
307 result = client_buffer_size - client_buffer_used; | |
308 pthread_mutex_unlock(&buffer_mutex); | |
309 | |
310 return result; | |
311 } | |
312 | |
313 // plays 'len' bytes of 'data' | |
314 // it should round it down to outburst*n | |
315 // return: number of bytes played | |
316 static int play(void* data,int len,int flags){ | |
317 writeBuffer(data, len); | |
318 // printf("ao_nas: wrote %d bytes of audio data\n", len); | |
319 return len; | |
320 } | |
321 | |
322 // return: delay in seconds between first and last sample in buffer | |
323 static float get_delay() | |
324 { | |
325 float result; | |
326 | |
327 pthread_mutex_lock(&buffer_mutex); | |
328 result = ((float)(client_buffer_used + server_buffer_used)) / | |
329 (float)ao_data.bps; | |
330 pthread_mutex_unlock(&buffer_mutex); | |
331 | |
332 return result; | |
333 } |