100l, mixed up ao_data.samplerate and ao_data.bps when calculating sleep time.
[mplayer/glamo.git] / libao2 / ao_nas.c
blob92ca56a35fe8a03ee6a4f6f62bd3d157e6fd3958
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 /* NAS_FRAG_SIZE must be a power-of-two value */
45 #define NAS_FRAG_SIZE 4096
47 static const char * const nas_event_types[] = {
48 "Undefined",
49 "Undefined",
50 "ElementNotify",
51 "GrabNotify",
52 "MonitorNotify",
53 "BucketNotify",
54 "DeviceNotify"
57 static const char * const nas_elementnotify_kinds[] = {
58 "LowWater",
59 "HighWater",
60 "State",
61 "Unknown"
64 static const char * const nas_states[] = {
65 "Stop",
66 "Start",
67 "Pause",
68 "Any"
71 static const char * const nas_reasons[] = {
72 "User",
73 "Underrun",
74 "Overrun",
75 "EOF",
76 "Watermark",
77 "Hardware",
78 "Any"
81 static const char* nas_reason(unsigned int reason)
83 if (reason > 6) reason = 6;
84 return nas_reasons[reason];
87 static const char* nas_elementnotify_kind(unsigned int kind)
89 if (kind > 2) kind = 3;
90 return nas_elementnotify_kinds[kind];
93 static const char* nas_event_type(unsigned int type) {
94 if (type > 6) type = 0;
95 return nas_event_types[type];
98 static const char* nas_state(unsigned int state) {
99 if (state>3) state = 3;
100 return nas_states[state];
103 static ao_info_t info =
105 "NAS audio output",
106 "nas",
107 "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
111 struct ao_nas_data {
112 AuServer *aud;
113 AuFlowID flow;
114 AuDeviceID dev;
115 AuFixedPoint gain;
117 unsigned int state;
118 int expect_underrun;
120 char *client_buffer;
121 char *server_buffer;
122 unsigned int client_buffer_size;
123 unsigned int client_buffer_used;
124 unsigned int server_buffer_size;
125 unsigned int server_buffer_used;
126 pthread_mutex_t buffer_mutex;
128 pthread_t event_thread;
129 int stop_thread;
132 static struct ao_nas_data *nas_data;
134 LIBAO_EXTERN(nas)
136 static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as)
138 char s[100];
139 AuGetErrorText(aud, as, s, 100);
140 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
143 static int nas_readBuffer(struct ao_nas_data *nas_data, unsigned int num)
145 AuStatus as;
147 pthread_mutex_lock(&nas_data->buffer_mutex);
148 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
149 num,
150 nas_data->client_buffer_used, nas_data->client_buffer_size,
151 nas_data->server_buffer_used, nas_data->server_buffer_size);
153 if (nas_data->client_buffer_used == 0) {
154 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");
155 pthread_mutex_unlock(&nas_data->buffer_mutex);
156 return 0;
158 if (num > nas_data->client_buffer_used)
159 num = nas_data->client_buffer_used;
162 * It is not appropriate to call AuWriteElement() here because the
163 * buffer is locked and delays writing to the network will cause
164 * other threads to block waiting for buffer_mutex. Instead the
165 * data is copied to "server_buffer" and written to the network
166 * outside of the locked section of code.
168 * (Note: Rather than these two buffers, a single circular buffer
169 * could eliminate the memcpy/memmove steps.)
171 /* make sure we don't overflow the buffer */
172 if (num > nas_data->server_buffer_size)
173 num = nas_data->server_buffer_size;
174 memcpy(nas_data->server_buffer, nas_data->client_buffer, num);
176 nas_data->client_buffer_used -= num;
177 nas_data->server_buffer_used += num;
178 memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
179 pthread_mutex_unlock(&nas_data->buffer_mutex);
182 * Now write the new buffer to the network.
184 AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);
185 if (as != AuSuccess)
186 nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
188 return num;
191 static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, unsigned int len)
193 pthread_mutex_lock(&nas_data->buffer_mutex);
194 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
195 len, nas_data->client_buffer_used, nas_data->client_buffer_size,
196 nas_data->server_buffer_used, nas_data->server_buffer_size);
198 /* make sure we don't overflow the buffer */
199 if (len > nas_data->client_buffer_size - nas_data->client_buffer_used)
200 len = nas_data->client_buffer_size - nas_data->client_buffer_used;
201 memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
202 nas_data->client_buffer_used += len;
204 pthread_mutex_unlock(&nas_data->buffer_mutex);
206 return len;
209 static int nas_empty_event_queue(struct ao_nas_data *nas_data)
211 AuEvent ev;
212 int result = 0;
214 while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
215 AuTrue, AuEventTypeElementNotify, &ev)) {
216 AuDispatchEvent(nas_data->aud, &ev);
217 result = 1;
219 return result;
222 static void *nas_event_thread_start(void *data)
224 struct ao_nas_data *nas_data = data;
226 do {
227 mp_msg(MSGT_AO, MSGL_DBG2,
228 "ao_nas: event thread heartbeat (state=%s)\n",
229 nas_state(nas_data->state));
230 nas_empty_event_queue(nas_data);
231 usleep(1000);
232 } while (!nas_data->stop_thread);
234 return NULL;
237 static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
239 char s[100];
240 AuGetErrorText(aud, ev->error_code, s, 100);
241 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"
242 "error_code: %d\n"
243 "request_code: %d\n"
244 "minor_code: %d\n",
246 ev->error_code,
247 ev->request_code,
248 ev->minor_code);
250 return AuTrue;
253 static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
255 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
256 struct ao_nas_data *nas_data = hnd->data;
258 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",
259 nas_event_type(event->type),
260 nas_elementnotify_kind(event->kind),
261 nas_state(event->prev_state),
262 nas_state(event->cur_state),
263 nas_reason(event->reason),
264 (int)event->num_bytes,
265 nas_data->expect_underrun);
267 if (event->num_bytes > INT_MAX) {
268 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n");
271 if (event->num_bytes > nas_data->server_buffer_used)
272 event->num_bytes = nas_data->server_buffer_used;
273 nas_data->server_buffer_used -= event->num_bytes;
275 switch (event->reason) {
276 case AuReasonWatermark:
277 nas_readBuffer(nas_data, event->num_bytes);
278 break;
279 case AuReasonUnderrun:
280 // buffer underrun -> refill buffer
281 nas_data->server_buffer_used = 0;
282 if (nas_data->expect_underrun) {
283 nas_data->expect_underrun = 0;
284 } else {
285 static int hint = 1;
286 mp_msg(MSGT_AO, MSGL_WARN,
287 "ao_nas: Buffer underrun.\n");
288 if (hint) {
289 hint = 0;
290 mp_msg(MSGT_AO, MSGL_HINT,
291 "Possible reasons are:\n"
292 "1) Network congestion.\n"
293 "2) Your NAS server is too slow.\n"
294 "Try renicing your nasd to e.g. -15.\n");
297 if (nas_readBuffer(nas_data,
298 nas_data->server_buffer_size -
299 nas_data->server_buffer_used) != 0) {
300 event->cur_state = AuStateStart;
301 break;
303 mp_msg(MSGT_AO, MSGL_DBG2,
304 "ao_nas: Can't refill buffer, stopping flow.\n");
305 AuStopFlow(aud, nas_data->flow, NULL);
306 break;
307 default:
308 break;
310 nas_data->state=event->cur_state;
311 return AuTrue;
314 static AuDeviceID nas_find_device(AuServer *aud, int nch)
316 int i;
317 for (i = 0; i < AuServerNumDevices(aud); i++) {
318 AuDeviceAttributes *dev = AuServerDevice(aud, i);
319 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
320 AuDeviceNumTracks(dev) == nch) {
321 return AuDeviceIdentifier(dev);
324 return AuNone;
327 static unsigned int nas_aformat_to_auformat(unsigned int *format)
329 switch (*format) {
330 case AF_FORMAT_U8:
331 return AuFormatLinearUnsigned8;
332 case AF_FORMAT_S8:
333 return AuFormatLinearSigned8;
334 case AF_FORMAT_U16_LE:
335 return AuFormatLinearUnsigned16LSB;
336 case AF_FORMAT_U16_BE:
337 return AuFormatLinearUnsigned16MSB;
338 case AF_FORMAT_S16_LE:
339 return AuFormatLinearSigned16LSB;
340 case AF_FORMAT_S16_BE:
341 return AuFormatLinearSigned16MSB;
342 case AF_FORMAT_MU_LAW:
343 return AuFormatULAW8;
344 default:
345 *format=AF_FORMAT_S16_NE;
346 return nas_aformat_to_auformat(format);
350 // to set/get/query special features/parameters
351 static int control(int cmd, void *arg)
353 AuElementParameters aep;
354 AuStatus as;
355 int retval = CONTROL_UNKNOWN;
357 ao_control_vol_t *vol = (ao_control_vol_t *)arg;
359 switch (cmd) {
360 case AOCONTROL_GET_VOLUME:
362 vol->right = (float)nas_data->gain/AU_FIXED_POINT_SCALE*50;
363 vol->left = vol->right;
365 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_GET_VOLUME: %.2f\n", vol->right);
366 retval = CONTROL_OK;
367 break;
369 case AOCONTROL_SET_VOLUME:
371 * kn: we should have vol->left == vol->right but i don't
372 * know if something can change it outside of ao_nas
373 * so i take the mean of both values.
375 nas_data->gain = AU_FIXED_POINT_SCALE*((vol->left+vol->right)/2)/50;
376 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_SET_VOLUME: %.2f\n", (vol->left+vol->right)/2);
378 aep.parameters[AuParmsMultiplyConstantConstant]=nas_data->gain;
379 aep.flow = nas_data->flow;
380 aep.element_num = 1;
381 aep.num_parameters = AuParmsMultiplyConstant;
383 AuSetElementParameters(nas_data->aud, 1, &aep, &as);
384 if (as != AuSuccess) {
385 nas_print_error(nas_data->aud,
386 "control(): AuSetElementParameters", as);
387 retval = CONTROL_ERROR;
388 } else retval = CONTROL_OK;
389 break;
392 return retval;
395 // open & setup audio device
396 // return: 1=success 0=fail
397 static int init(int rate,int channels,int format,int flags)
399 AuElement elms[3];
400 AuStatus as;
401 unsigned char auformat = nas_aformat_to_auformat(&format);
402 int bytes_per_sample = channels * AuSizeofFormat(auformat);
403 int buffer_size;
404 char *server;
406 (void)flags; /* shut up 'unused parameter' warning */
408 nas_data=malloc(sizeof(struct ao_nas_data));
409 memset(nas_data, 0, sizeof(struct ao_nas_data));
411 mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n",rate,channels,
412 af_fmt2str_short(format));
414 ao_data.format = format;
415 ao_data.samplerate = rate;
416 ao_data.channels = channels;
417 ao_data.outburst = NAS_FRAG_SIZE;
418 ao_data.bps = rate * bytes_per_sample;
419 buffer_size = ao_data.bps; /* buffer 1 second */
421 * round up to multiple of NAS_FRAG_SIZE
422 * divide by 3 first because of 2:1 split
424 buffer_size = (buffer_size/3 + NAS_FRAG_SIZE-1) & ~(NAS_FRAG_SIZE-1);
425 ao_data.buffersize = buffer_size*3;
427 nas_data->client_buffer_size = buffer_size*2;
428 nas_data->client_buffer = malloc(nas_data->client_buffer_size);
429 nas_data->server_buffer_size = buffer_size;
430 nas_data->server_buffer = malloc(nas_data->server_buffer_size);
432 if (!bytes_per_sample) {
433 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Zero bytes per sample -> nosound\n");
434 return 0;
437 if (!(server = getenv("AUDIOSERVER")) &&
438 !(server = getenv("DISPLAY"))) {
439 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): AUDIOSERVER environment variable not set -> nosound\n");
440 return 0;
443 mp_msg(MSGT_AO, MSGL_V, "ao_nas: init(): Using audioserver %s\n", server);
445 nas_data->aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
446 if (!nas_data->aud) {
447 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't open nas audio server -> nosound\n");
448 return 0;
451 while (channels>0) {
452 nas_data->dev = nas_find_device(nas_data->aud, channels);
453 if (nas_data->dev != AuNone &&
454 ((nas_data->flow = AuCreateFlow(nas_data->aud, NULL)) != 0))
455 break;
456 channels--;
459 if (nas_data->flow == 0) {
460 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't find a suitable output device -> nosound\n");
461 AuCloseServer(nas_data->aud);
462 nas_data->aud = 0;
463 return 0;
466 AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
467 buffer_size / bytes_per_sample,
468 (buffer_size - NAS_FRAG_SIZE) /
469 bytes_per_sample, 0, NULL);
470 nas_data->gain = AuFixedPointFromFraction(1, 1);
471 AuMakeElementMultiplyConstant(elms+1, 0, nas_data->gain);
472 AuMakeElementExportDevice(elms+2, 1, nas_data->dev, rate,
473 AuUnlimitedSamples, 0, NULL);
474 AuSetElements(nas_data->aud, nas_data->flow, AuTrue, sizeof(elms)/sizeof(*elms), elms, &as);
475 if (as != AuSuccess) {
476 nas_print_error(nas_data->aud, "init(): AuSetElements", as);
477 AuCloseServer(nas_data->aud);
478 nas_data->aud = 0;
479 return 0;
481 AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask |
482 AuEventHandlerTypeMask,
483 AuEventTypeElementNotify, nas_data->flow,
484 nas_event_handler, (AuPointer) nas_data);
485 AuSetErrorHandler(nas_data->aud, nas_error_handler);
486 nas_data->state=AuStateStop;
487 nas_data->expect_underrun=0;
489 pthread_mutex_init(&nas_data->buffer_mutex, NULL);
490 pthread_create(&nas_data->event_thread, NULL, &nas_event_thread_start, nas_data);
492 return 1;
495 // close audio device
496 static void uninit(int immed){
498 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n");
500 nas_data->expect_underrun = 1;
501 if (!immed)
502 while (nas_data->state != AuStateStop) usleep(1000);
503 nas_data->stop_thread = 1;
504 pthread_join(nas_data->event_thread, NULL);
505 AuCloseServer(nas_data->aud);
506 nas_data->aud = 0;
507 free(nas_data->client_buffer);
508 free(nas_data->server_buffer);
511 // stop playing and empty buffers (for seeking/pause)
512 static void reset(void){
513 AuStatus as;
515 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: reset()\n");
517 pthread_mutex_lock(&nas_data->buffer_mutex);
518 nas_data->client_buffer_used = 0;
519 pthread_mutex_unlock(&nas_data->buffer_mutex);
520 while (nas_data->state != AuStateStop) {
521 AuStopFlow(nas_data->aud, nas_data->flow, &as);
522 if (as != AuSuccess)
523 nas_print_error(nas_data->aud, "reset(): AuStopFlow", as);
524 usleep(1000);
528 // stop playing, keep buffers (for pause)
529 static void audio_pause(void)
531 AuStatus as;
532 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_pause()\n");
534 AuStopFlow(nas_data->aud, nas_data->flow, &as);
537 // resume playing, after audio_pause()
538 static void audio_resume(void)
540 AuStatus as;
542 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n");
544 AuStartFlow(nas_data->aud, nas_data->flow, &as);
545 if (as != AuSuccess)
546 nas_print_error(nas_data->aud,
547 "play(): AuStartFlow", as);
551 // return: how many bytes can be played without blocking
552 static int get_space(void)
554 int result;
556 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_space()\n");
558 pthread_mutex_lock(&nas_data->buffer_mutex);
559 result = nas_data->client_buffer_size - nas_data->client_buffer_used;
560 pthread_mutex_unlock(&nas_data->buffer_mutex);
562 return result;
565 // plays 'len' bytes of 'data'
566 // it should round it down to outburst*n
567 // return: number of bytes played
568 static int play(void* data,int len,int flags)
570 int written, maxbursts = 0, playbursts = 0;
571 AuStatus as;
573 mp_msg(MSGT_AO, MSGL_DBG3,
574 "ao_nas: play(%p, %d, %d)\n",
575 data, len, flags);
577 if (len == 0)
578 return 0;
580 if (!(flags & AOPLAY_FINAL_CHUNK)) {
581 pthread_mutex_lock(&nas_data->buffer_mutex);
582 maxbursts = (nas_data->client_buffer_size -
583 nas_data->client_buffer_used) / ao_data.outburst;
584 playbursts = len / ao_data.outburst;
585 len = (playbursts > maxbursts ? maxbursts : playbursts) *
586 ao_data.outburst;
587 pthread_mutex_unlock(&nas_data->buffer_mutex);
591 * If AOPLAY_FINAL_CHUNK is set, we did not actually check len fits
592 * into the available buffer space, but mplayer.c shouldn't give us
593 * more to play than we report to it by get_space(), so this should be
594 * fine.
596 written = nas_writeBuffer(nas_data, data, len);
598 if (nas_data->state != AuStateStart &&
599 (maxbursts == playbursts ||
600 flags & AOPLAY_FINAL_CHUNK)) {
601 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play(): Starting flow.\n");
602 nas_data->expect_underrun = 1;
603 AuStartFlow(nas_data->aud, nas_data->flow, &as);
604 if (as != AuSuccess)
605 nas_print_error(nas_data->aud, "play(): AuStartFlow", as);
608 return written;
611 // return: delay in seconds between first and last sample in buffer
612 static float get_delay(void)
614 float result;
616 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_delay()\n");
618 pthread_mutex_lock(&nas_data->buffer_mutex);
619 result = ((float)(nas_data->client_buffer_used +
620 nas_data->server_buffer_used)) /
621 (float)ao_data.bps;
622 pthread_mutex_unlock(&nas_data->buffer_mutex);
624 return result;