Add explanatory comments to the #endif part of multiple inclusion guards.
[mplayer/greg.git] / libao2 / ao_nas.c
blob4888cfc366f6594ada7c28a90ad35c8cb2b0db3d
1 /*
2 * NAS output plugin for mplayer
4 * based on the libaudiooss parts rewritten by me, which were
5 * originally based on the NAS output plugin for xmms.
7 * xmms plugin by Willem Monsuwe
8 * adapted for libaudiooss by Jon Trulson
9 * further modified by Erik Inge Bolsø
10 * largely rewritten and used for this
11 * plugin by Tobias Diedrich
13 * Theory of operation:
15 * The NAS consists of two parts, a server daemon and a client.
16 * We setup the server to use a buffer of size bytes_per_second
17 * with a low watermark of buffer_size - NAS_FRAG_SIZE.
18 * Upon starting the flow the server will generate a buffer underrun
19 * event and the event handler will fill the buffer for the first time.
20 * Now the server will generate a lowwater event when the server buffer
21 * falls below the low watermark value. The event handler gets called
22 * again and refills the buffer by the number of bytes requested by the
23 * server (usually a multiple of 4096). To prevent stuttering on
24 * startup (start of playing, seeks, unpausing) the client buffer should
25 * be bigger than the server buffer. (For debugging we also do some
26 * accounting of what we think how much of the server buffer is filled)
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <pthread.h>
34 #include <limits.h>
35 #include <audio/audiolib.h>
37 #include "config.h"
38 #include "mp_msg.h"
40 #include "audio_out.h"
41 #include "audio_out_internal.h"
42 #include "libaf/af_format.h"
44 #define NAS_FRAG_SIZE 4096
46 static char *nas_event_types[] = {
47 "Undefined",
48 "Undefined",
49 "ElementNotify",
50 "GrabNotify",
51 "MonitorNotify",
52 "BucketNotify",
53 "DeviceNotify"
56 static char *nas_elementnotify_kinds[] = {
57 "LowWater",
58 "HighWater",
59 "State",
60 "Unknown"
63 static char *nas_states[] = {
64 "Stop",
65 "Start",
66 "Pause",
67 "Any"
70 static char *nas_reasons[] = {
71 "User",
72 "Underrun",
73 "Overrun",
74 "EOF",
75 "Watermark",
76 "Hardware",
77 "Any"
80 static char* nas_reason(unsigned int reason)
82 if (reason > 6) reason = 6;
83 return nas_reasons[reason];
86 static char* nas_elementnotify_kind(unsigned int kind)
88 if (kind > 2) kind = 3;
89 return nas_elementnotify_kinds[kind];
92 static char* nas_event_type(unsigned int type) {
93 if (type > 6) type = 0;
94 return nas_event_types[type];
97 static char* nas_state(unsigned int state) {
98 if (state>3) state = 3;
99 return nas_states[state];
102 static ao_info_t info =
104 "NAS audio output",
105 "nas",
106 "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
110 struct ao_nas_data {
111 AuServer *aud;
112 AuFlowID flow;
113 AuDeviceID dev;
114 AuFixedPoint gain;
116 unsigned int state;
117 int expect_underrun;
119 void *client_buffer;
120 void *server_buffer;
121 unsigned int client_buffer_size;
122 unsigned int client_buffer_used;
123 unsigned int server_buffer_size;
124 unsigned int server_buffer_used;
125 pthread_mutex_t buffer_mutex;
127 pthread_t event_thread;
128 int stop_thread;
131 static struct ao_nas_data *nas_data;
133 LIBAO_EXTERN(nas)
135 static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as)
137 char s[100];
138 AuGetErrorText(aud, as, s, 100);
139 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
142 static int nas_readBuffer(struct ao_nas_data *nas_data, int num)
144 AuStatus as;
146 pthread_mutex_lock(&nas_data->buffer_mutex);
147 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
148 num,
149 nas_data->client_buffer_used, nas_data->client_buffer_size,
150 nas_data->server_buffer_used, nas_data->server_buffer_size);
152 if (nas_data->client_buffer_used == 0) {
153 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");
154 pthread_mutex_unlock(&nas_data->buffer_mutex);
155 return 0;
157 if (num > nas_data->client_buffer_used)
158 num = nas_data->client_buffer_used;
161 * It is not appropriate to call AuWriteElement() here because the
162 * buffer is locked and delays writing to the network will cause
163 * other threads to block waiting for buffer_mutex. Instead the
164 * data is copied to "server_buffer" and written to the network
165 * outside of the locked section of code.
167 * (Note: Rather than these two buffers, a single circular buffer
168 * could eliminate the memcpy/memmove steps.)
170 /* make sure we don't overflow the buffer */
171 if (num > nas_data->server_buffer_size)
172 num = nas_data->server_buffer_size;
173 memcpy(nas_data->server_buffer, nas_data->client_buffer, num);
175 nas_data->client_buffer_used -= num;
176 nas_data->server_buffer_used += num;
177 memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
178 pthread_mutex_unlock(&nas_data->buffer_mutex);
181 * Now write the new buffer to the network.
183 AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);
184 if (as != AuSuccess)
185 nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
187 return num;
190 static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, int len)
192 pthread_mutex_lock(&nas_data->buffer_mutex);
193 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
194 len, nas_data->client_buffer_used, nas_data->client_buffer_size,
195 nas_data->server_buffer_used, nas_data->server_buffer_size);
197 /* make sure we don't overflow the buffer */
198 if (len > nas_data->client_buffer_size - nas_data->client_buffer_used)
199 len = nas_data->client_buffer_size - nas_data->client_buffer_used;
200 memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
201 nas_data->client_buffer_used += len;
203 pthread_mutex_unlock(&nas_data->buffer_mutex);
205 return len;
208 static int nas_empty_event_queue(struct ao_nas_data *nas_data)
210 AuEvent ev;
211 int result = 0;
213 while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
214 AuTrue, AuEventTypeElementNotify, &ev)) {
215 AuDispatchEvent(nas_data->aud, &ev);
216 result = 1;
218 return result;
221 static void *nas_event_thread_start(void *data)
223 struct ao_nas_data *nas_data = data;
225 do {
226 mp_msg(MSGT_AO, MSGL_DBG2,
227 "ao_nas: event thread heartbeat (state=%s)\n",
228 nas_state(nas_data->state));
229 nas_empty_event_queue(nas_data);
230 usleep(1000);
231 } while (!nas_data->stop_thread);
233 return NULL;
236 static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
238 char s[100];
239 AuGetErrorText(aud, ev->error_code, s, 100);
240 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"
241 "error_code: %d\n"
242 "request_code: %d\n"
243 "minor_code: %d\n",
245 ev->error_code,
246 ev->request_code,
247 ev->minor_code);
249 return AuTrue;
252 static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
254 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
255 struct ao_nas_data *nas_data = hnd->data;
257 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: event_handler(): type %s kind %s state %s->%s reason %s numbytes %d expect_underrun %d\n",
258 nas_event_type(event->type),
259 nas_elementnotify_kind(event->kind),
260 nas_state(event->prev_state),
261 nas_state(event->cur_state),
262 nas_reason(event->reason),
263 event->num_bytes,
264 nas_data->expect_underrun);
266 if (event->num_bytes > INT_MAX) {
267 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n");
270 if (event->num_bytes > nas_data->server_buffer_used)
271 event->num_bytes = nas_data->server_buffer_used;
272 nas_data->server_buffer_used -= event->num_bytes;
274 switch (event->reason) {
275 case AuReasonWatermark:
276 nas_readBuffer(nas_data, event->num_bytes);
277 break;
278 case AuReasonUnderrun:
279 // buffer underrun -> refill buffer
280 nas_data->server_buffer_used = 0;
281 if (nas_data->expect_underrun) {
282 nas_data->expect_underrun = 0;
283 } else {
284 static int hint = 1;
285 mp_msg(MSGT_AO, MSGL_WARN,
286 "ao_nas: Buffer underrun.\n");
287 if (hint) {
288 hint = 0;
289 mp_msg(MSGT_AO, MSGL_HINT,
290 "Possible reasons are:\n"
291 "1) Network congestion.\n"
292 "2) Your NAS server is too slow.\n"
293 "Try renicing your nasd to e.g. -15.\n");
296 if (nas_readBuffer(nas_data,
297 nas_data->server_buffer_size -
298 nas_data->server_buffer_used) != 0) {
299 event->cur_state = AuStateStart;
300 break;
302 mp_msg(MSGT_AO, MSGL_DBG2,
303 "ao_nas: Can't refill buffer, stopping flow.\n");
304 AuStopFlow(nas_data->aud, nas_data->flow, NULL);
305 break;
306 default:
307 break;
309 nas_data->state=event->cur_state;
310 return AuTrue;
313 static AuDeviceID nas_find_device(AuServer *aud, int nch)
315 int i;
316 for (i = 0; i < AuServerNumDevices(aud); i++) {
317 AuDeviceAttributes *dev = AuServerDevice(aud, i);
318 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
319 AuDeviceNumTracks(dev) == nch) {
320 return AuDeviceIdentifier(dev);
323 return AuNone;
326 static unsigned int nas_aformat_to_auformat(unsigned int *format)
328 switch (*format) {
329 case AF_FORMAT_U8:
330 return AuFormatLinearUnsigned8;
331 case AF_FORMAT_S8:
332 return AuFormatLinearSigned8;
333 case AF_FORMAT_U16_LE:
334 return AuFormatLinearUnsigned16LSB;
335 case AF_FORMAT_U16_BE:
336 return AuFormatLinearUnsigned16MSB;
337 case AF_FORMAT_S16_LE:
338 return AuFormatLinearSigned16LSB;
339 case AF_FORMAT_S16_BE:
340 return AuFormatLinearSigned16MSB;
341 case AF_FORMAT_MU_LAW:
342 return AuFormatULAW8;
343 default:
344 *format=AF_FORMAT_S16_NE;
345 return nas_aformat_to_auformat(format);
349 // to set/get/query special features/parameters
350 static int control(int cmd, void *arg)
352 AuElementParameters aep;
353 AuStatus as;
354 int retval = CONTROL_UNKNOWN;
356 ao_control_vol_t *vol = (ao_control_vol_t *)arg;
358 switch (cmd) {
359 case AOCONTROL_GET_VOLUME:
361 vol->right = (float)nas_data->gain/AU_FIXED_POINT_SCALE*50;
362 vol->left = vol->right;
364 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_GET_VOLUME: %08x\n", nas_data->gain);
365 retval = CONTROL_OK;
366 break;
368 case AOCONTROL_SET_VOLUME:
370 * kn: we should have vol->left == vol->right but i don't
371 * know if something can change it outside of ao_nas
372 * so i take the mean of both values.
374 nas_data->gain = AU_FIXED_POINT_SCALE*((vol->left+vol->right)/2)/50;
375 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_SET_VOLUME: %08x\n", nas_data->gain);
377 aep.parameters[AuParmsMultiplyConstantConstant]=nas_data->gain;
378 aep.flow = nas_data->flow;
379 aep.element_num = 1;
380 aep.num_parameters = AuParmsMultiplyConstant;
382 AuSetElementParameters(nas_data->aud, 1, &aep, &as);
383 if (as != AuSuccess) {
384 nas_print_error(nas_data->aud,
385 "control(): AuSetElementParameters", as);
386 retval = CONTROL_ERROR;
387 } else retval = CONTROL_OK;
388 break;
391 return retval;
394 // open & setup audio device
395 // return: 1=success 0=fail
396 static int init(int rate,int channels,int format,int flags)
398 AuElement elms[3];
399 AuStatus as;
400 unsigned char auformat = nas_aformat_to_auformat(&format);
401 int bytes_per_sample = channels * AuSizeofFormat(auformat);
402 int buffer_size;
403 char *server;
405 nas_data=malloc(sizeof(struct ao_nas_data));
406 memset(nas_data, 0, sizeof(struct ao_nas_data));
408 mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n",rate,channels,
409 af_fmt2str_short(format));
411 ao_data.format = format;
412 ao_data.samplerate = rate;
413 ao_data.channels = channels;
414 ao_data.outburst = NAS_FRAG_SIZE;
415 ao_data.bps = rate * bytes_per_sample;
416 buffer_size = ao_data.bps; /* buffer 1 second */
418 * round up to multiple of NAS_FRAG_SIZE
419 * divide by 3 first because of 2:1 split
421 buffer_size = (buffer_size/3 + NAS_FRAG_SIZE-1) & ~(NAS_FRAG_SIZE-1);
422 ao_data.buffersize = buffer_size*3;
424 nas_data->client_buffer_size = buffer_size*2;
425 nas_data->client_buffer = malloc(nas_data->client_buffer_size);
426 nas_data->server_buffer_size = buffer_size;
427 nas_data->server_buffer = malloc(nas_data->server_buffer_size);
429 if (!bytes_per_sample) {
430 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Zero bytes per sample -> nosound\n");
431 return 0;
434 if (!(server = getenv("AUDIOSERVER")) &&
435 !(server = getenv("DISPLAY"))) {
436 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): AUDIOSERVER environment variable not set -> nosound\n");
437 return 0;
440 mp_msg(MSGT_AO, MSGL_V, "ao_nas: init(): Using audioserver %s\n", server);
442 nas_data->aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
443 if (!nas_data->aud) {
444 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't open nas audio server -> nosound\n");
445 return 0;
448 while (channels>0) {
449 nas_data->dev = nas_find_device(nas_data->aud, channels);
450 if (nas_data->dev != AuNone &&
451 ((nas_data->flow = AuCreateFlow(nas_data->aud, NULL)) != 0))
452 break;
453 channels--;
456 if (nas_data->flow == 0) {
457 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't find a suitable output device -> nosound\n");
458 AuCloseServer(nas_data->aud);
459 nas_data->aud = 0;
460 return 0;
463 AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
464 buffer_size / bytes_per_sample,
465 (buffer_size - NAS_FRAG_SIZE) /
466 bytes_per_sample, 0, NULL);
467 nas_data->gain = AuFixedPointFromFraction(1, 1);
468 AuMakeElementMultiplyConstant(elms+1, 0, nas_data->gain);
469 AuMakeElementExportDevice(elms+2, 1, nas_data->dev, rate,
470 AuUnlimitedSamples, 0, NULL);
471 AuSetElements(nas_data->aud, nas_data->flow, AuTrue, sizeof(elms)/sizeof(*elms), elms, &as);
472 if (as != AuSuccess) {
473 nas_print_error(nas_data->aud, "init(): AuSetElements", as);
474 AuCloseServer(nas_data->aud);
475 nas_data->aud = 0;
476 return 0;
478 AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask |
479 AuEventHandlerTypeMask,
480 AuEventTypeElementNotify, nas_data->flow,
481 nas_event_handler, (AuPointer) nas_data);
482 AuSetErrorHandler(nas_data->aud, nas_error_handler);
483 nas_data->state=AuStateStop;
484 nas_data->expect_underrun=0;
486 pthread_mutex_init(&nas_data->buffer_mutex, NULL);
487 pthread_create(&nas_data->event_thread, NULL, &nas_event_thread_start, nas_data);
489 return 1;
492 // close audio device
493 static void uninit(int immed){
495 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n");
497 nas_data->expect_underrun = 1;
498 if (!immed)
499 while (nas_data->state != AuStateStop) usleep(1000);
500 nas_data->stop_thread = 1;
501 pthread_join(nas_data->event_thread, NULL);
502 AuCloseServer(nas_data->aud);
503 nas_data->aud = 0;
504 free(nas_data->client_buffer);
505 free(nas_data->server_buffer);
508 // stop playing and empty buffers (for seeking/pause)
509 static void reset(void){
510 AuStatus as;
512 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: reset()\n");
514 pthread_mutex_lock(&nas_data->buffer_mutex);
515 nas_data->client_buffer_used = 0;
516 pthread_mutex_unlock(&nas_data->buffer_mutex);
517 while (nas_data->state != AuStateStop) {
518 AuStopFlow(nas_data->aud, nas_data->flow, &as);
519 if (as != AuSuccess)
520 nas_print_error(nas_data->aud, "reset(): AuStopFlow", as);
521 usleep(1000);
525 // stop playing, keep buffers (for pause)
526 static void audio_pause(void)
528 AuStatus as;
529 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_pause()\n");
531 AuStopFlow(nas_data->aud, nas_data->flow, &as);
534 // resume playing, after audio_pause()
535 static void audio_resume(void)
537 AuStatus as;
539 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n");
541 AuStartFlow(nas_data->aud, nas_data->flow, &as);
542 if (as != AuSuccess)
543 nas_print_error(nas_data->aud,
544 "play(): AuStartFlow", as);
548 // return: how many bytes can be played without blocking
549 static int get_space(void)
551 int result;
553 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_space()\n");
555 pthread_mutex_lock(&nas_data->buffer_mutex);
556 result = nas_data->client_buffer_size - nas_data->client_buffer_used;
557 pthread_mutex_unlock(&nas_data->buffer_mutex);
559 return result;
562 // plays 'len' bytes of 'data'
563 // it should round it down to outburst*n
564 // return: number of bytes played
565 static int play(void* data,int len,int flags)
567 int maxbursts, playbursts, writelen;
568 AuStatus as;
570 mp_msg(MSGT_AO, MSGL_DBG3,
571 "ao_nas: play(%p, %d, %d)\n",
572 data, len, flags);
574 if (len == 0)
575 return 0;
577 pthread_mutex_lock(&nas_data->buffer_mutex);
578 maxbursts = (nas_data->client_buffer_size -
579 nas_data->client_buffer_used) / ao_data.outburst;
580 playbursts = len / ao_data.outburst;
581 writelen = (playbursts > maxbursts ? maxbursts : playbursts) *
582 ao_data.outburst;
583 pthread_mutex_unlock(&nas_data->buffer_mutex);
585 writelen = nas_writeBuffer(nas_data, data, writelen);
587 if (nas_data->state != AuStateStart &&
588 maxbursts == playbursts) {
589 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play(): Starting flow.\n");
590 nas_data->expect_underrun = 1;
591 AuStartFlow(nas_data->aud, nas_data->flow, &as);
592 if (as != AuSuccess)
593 nas_print_error(nas_data->aud, "play(): AuStartFlow", as);
596 return writelen;
599 // return: delay in seconds between first and last sample in buffer
600 static float get_delay(void)
602 float result;
604 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_delay()\n");
606 pthread_mutex_lock(&nas_data->buffer_mutex);
607 result = ((float)(nas_data->client_buffer_used +
608 nas_data->server_buffer_used)) /
609 (float)ao_data.bps;
610 pthread_mutex_unlock(&nas_data->buffer_mutex);
612 return result;