fix Cyberblade VidiX driver TVOUT patch by Benjamin Zores <ben@tutuxclan.org>
[mplayer.git] / libao2 / ao_nas.c
blobb3d8f1523aee66c70b7b16080bdb7f2a8e2402f9
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 <audio/audiolib.h>
36 #include "../mp_msg.h"
38 #include "audio_out.h"
39 #include "audio_out_internal.h"
40 #include "afmt.h"
42 #define NAS_FRAG_SIZE 4096
44 static char *nas_event_types[] = {
45 "Undefined",
46 "Undefined",
47 "ElementNotify",
48 "GrabNotify",
49 "MonitorNotify",
50 "BucketNotify",
51 "DeviceNotify"
54 static char *nas_elementnotify_kinds[] = {
55 "LowWater",
56 "HighWater",
57 "State",
58 "Unknown"
61 static char *nas_states[] = {
62 "Stop",
63 "Start",
64 "Pause",
65 "Any"
68 static char *nas_reasons[] = {
69 "User",
70 "Underrun",
71 "Overrun",
72 "EOF",
73 "Watermark",
74 "Hardware",
75 "Any"
78 static char* nas_reason(unsigned int reason)
80 if (reason > 6) reason = 6;
81 return nas_reasons[reason];
84 static char* nas_elementnotify_kind(unsigned int kind)
86 if (kind > 2) kind = 3;
87 return nas_elementnotify_kinds[kind];
90 static char* nas_event_type(unsigned int type) {
91 if (type > 6) type = 0;
92 return nas_event_types[type];
95 static char* nas_state(unsigned int state) {
96 if (state>3) state = 3;
97 return nas_states[state];
100 static ao_info_t info =
102 "NAS audio output",
103 "nas",
104 "Tobias Diedrich",
108 struct ao_nas_data {
109 AuServer *aud;
110 AuFlowID flow;
111 AuDeviceID dev;
112 AuFixedPoint gain;
114 unsigned int state;
115 int expect_underrun;
117 void *client_buffer;
118 void *server_buffer;
119 int client_buffer_size;
120 int client_buffer_used;
121 int server_buffer_size;
122 int server_buffer_used;
123 pthread_mutex_t buffer_mutex;
125 pthread_t event_thread;
126 int stop_thread;
129 static struct ao_nas_data *nas_data;
131 LIBAO_EXTERN(nas)
133 static void nas_print_error(AuServer *aud, char *prefix, AuStatus as)
135 char s[100];
136 AuGetErrorText(aud, as, s, 100);
137 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
140 static int nas_readBuffer(struct ao_nas_data *nas_data, int num)
142 AuStatus as;
144 pthread_mutex_lock(&nas_data->buffer_mutex);
145 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
146 num,
147 nas_data->client_buffer_used, nas_data->client_buffer_size,
148 nas_data->server_buffer_used, nas_data->server_buffer_size);
150 if (nas_data->client_buffer_used == 0) {
151 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");
152 pthread_mutex_unlock(&nas_data->buffer_mutex);
153 return 0;
155 if (nas_data->client_buffer_used < num)
156 num = nas_data->client_buffer_used;
159 * It is not appropriate to call AuWriteElement() here because the
160 * buffer is locked and delays writing to the network will cause
161 * other threads to block waiting for buffer_mutex. Instead the
162 * data is copied to "server_buffer" and written it to the network
163 * outside of the locked section of code.
165 * (Note: Rather than these two buffers, a single circular buffer
166 * could eliminate the memcpy/memmove steps.)
168 memcpy(nas_data->server_buffer, nas_data->client_buffer, num);
170 nas_data->client_buffer_used -= num;
171 nas_data->server_buffer_used += num;
172 memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
173 pthread_mutex_unlock(&nas_data->buffer_mutex);
176 * Now write the new buffer to the network.
178 AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);
179 if (as != AuSuccess)
180 nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
182 return num;
185 static void nas_writeBuffer(struct ao_nas_data *nas_data, void *data, int len)
187 pthread_mutex_lock(&nas_data->buffer_mutex);
188 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
189 len, nas_data->client_buffer_used, nas_data->client_buffer_size,
190 nas_data->server_buffer_used, nas_data->server_buffer_size);
192 memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
193 nas_data->client_buffer_used += len;
195 pthread_mutex_unlock(&nas_data->buffer_mutex);
198 static int nas_empty_event_queue(struct ao_nas_data *nas_data)
200 AuEvent ev;
201 int result = 0;
203 while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
204 AuTrue, AuEventTypeElementNotify, &ev)) {
205 AuDispatchEvent(nas_data->aud, &ev);
206 result = 1;
208 return result;
211 static void *nas_event_thread_start(void *data)
213 struct ao_nas_data *nas_data = data;
215 do {
216 mp_msg(MSGT_AO, MSGL_DBG2,
217 "ao_nas: event thread heartbeat (state=%s)\n",
218 nas_state(nas_data->state));
219 nas_empty_event_queue(nas_data);
220 usleep(1000);
221 } while (!nas_data->stop_thread);
223 return NULL;
226 static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
228 char s[100];
229 AuGetErrorText(aud, ev->error_code, s, 100);
230 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"
231 "error_code: %d\n"
232 "request_code: %d\n"
233 "minor_code: %d\n",
235 ev->error_code,
236 ev->request_code,
237 ev->minor_code);
239 return AuTrue;
242 static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
244 AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
245 struct ao_nas_data *nas_data = hnd->data;
247 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",
248 nas_event_type(event->type),
249 nas_elementnotify_kind(event->kind),
250 nas_state(event->prev_state),
251 nas_state(event->cur_state),
252 nas_reason(event->reason),
253 event->num_bytes,
254 nas_data->expect_underrun);
256 nas_data->server_buffer_used -= event->num_bytes;
257 if (nas_data->server_buffer_used < 0)
258 nas_data->server_buffer_used = 0;
260 switch (event->reason) {
261 case AuReasonWatermark:
262 nas_readBuffer(nas_data, event->num_bytes);
263 break;
264 case AuReasonUnderrun:
265 // buffer underrun -> refill buffer
266 nas_data->server_buffer_used = 0;
267 if (nas_data->expect_underrun) {
268 nas_data->expect_underrun = 0;
269 } else {
270 static int hint = 1;
271 mp_msg(MSGT_AO, MSGL_WARN,
272 "ao_nas: Buffer underrun.\n");
273 if (hint) {
274 hint = 0;
275 mp_msg(MSGT_AO, MSGL_HINT,
276 "Possible reasons are:\n"
277 "1) Network congestion.\n"
278 "2) Your NAS server is too slow.\n"
279 "Try renicing your nasd to e.g. -15.\n");
282 if (nas_readBuffer(nas_data,
283 nas_data->server_buffer_size -
284 nas_data->server_buffer_used) != 0) {
285 event->cur_state = AuStateStart;
286 break;
288 mp_msg(MSGT_AO, MSGL_DBG2,
289 "ao_nas: Can't refill buffer, stopping flow.\n");
290 AuStopFlow(nas_data->aud, nas_data->flow, NULL);
291 break;
292 default:
293 break;
295 nas_data->state=event->cur_state;
296 return AuTrue;
299 static AuDeviceID nas_find_device(AuServer *aud, int nch)
301 int i;
302 for (i = 0; i < AuServerNumDevices(aud); i++) {
303 AuDeviceAttributes *dev = AuServerDevice(aud, i);
304 if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
305 AuDeviceNumTracks(dev) == nch) {
306 return AuDeviceIdentifier(dev);
309 return AuNone;
312 static unsigned int nas_aformat_to_auformat(unsigned int *format)
314 switch (*format) {
315 case AFMT_U8:
316 return AuFormatLinearUnsigned8;
317 case AFMT_S8:
318 return AuFormatLinearSigned8;
319 case AFMT_U16_LE:
320 return AuFormatLinearUnsigned16LSB;
321 case AFMT_U16_BE:
322 return AuFormatLinearUnsigned16MSB;
323 case AFMT_S16_LE:
324 return AuFormatLinearSigned16LSB;
325 case AFMT_S16_BE:
326 return AuFormatLinearSigned16MSB;
327 case AFMT_MU_LAW:
328 return AuFormatULAW8;
329 default:
330 *format=AFMT_S16_NE;
331 return nas_aformat_to_auformat(format);
335 // to set/get/query special features/parameters
336 static int control(int cmd, void *arg)
338 AuElementParameters aep;
339 AuStatus as;
340 int retval = CONTROL_UNKNOWN;
342 ao_control_vol_t *vol = (ao_control_vol_t *)arg;
344 switch (cmd) {
345 case AOCONTROL_GET_VOLUME:
347 vol->right = (float)nas_data->gain/AU_FIXED_POINT_SCALE*50;
348 vol->left = vol->right;
350 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_GET_VOLUME: %08x\n", nas_data->gain);
351 retval = CONTROL_OK;
352 break;
354 case AOCONTROL_SET_VOLUME:
356 * kn: we should have vol->left == vol->right but i don't
357 * know if something can change it outside of ao_nas
358 * so i take the mean of both values.
360 nas_data->gain = AU_FIXED_POINT_SCALE*((vol->left+vol->right)/2)/50;
361 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_SET_VOLUME: %08x\n", nas_data->gain);
363 aep.parameters[AuParmsMultiplyConstantConstant]=nas_data->gain;
364 aep.flow = nas_data->flow;
365 aep.element_num = 1;
366 aep.num_parameters = AuParmsMultiplyConstant;
368 AuSetElementParameters(nas_data->aud, 1, &aep, &as);
369 if (as != AuSuccess) {
370 nas_print_error(nas_data->aud,
371 "control(): AuSetElementParameters", as);
372 retval = CONTROL_ERROR;
373 } else retval = CONTROL_OK;
374 break;
377 return retval;
380 // open & setup audio device
381 // return: 1=success 0=fail
382 static int init(int rate,int channels,int format,int flags)
384 AuElement elms[3];
385 AuStatus as;
386 unsigned char auformat = nas_aformat_to_auformat(&format);
387 int bytes_per_sample = channels * AuSizeofFormat(auformat);
388 int buffer_size;
389 char *server;
391 nas_data=malloc(sizeof(struct ao_nas_data));
392 memset(nas_data, 0, sizeof(struct ao_nas_data));
394 mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n",rate,channels,
395 audio_out_format_name(format));
397 ao_data.format = format;
398 ao_data.samplerate = rate;
399 ao_data.channels = channels;
400 ao_data.outburst = NAS_FRAG_SIZE;
401 ao_data.bps = rate * bytes_per_sample;
402 buffer_size = ao_data.bps; /* buffer 1 second */
404 * round up to multiple of NAS_FRAG_SIZE
405 * divide by 3 first because of 2:1 split
407 buffer_size = (buffer_size/3 + NAS_FRAG_SIZE-1) & ~(NAS_FRAG_SIZE-1);
408 ao_data.buffersize = buffer_size*3;
410 nas_data->client_buffer_size = buffer_size*2;
411 nas_data->client_buffer = malloc(nas_data->client_buffer_size);
412 nas_data->server_buffer_size = buffer_size;
413 nas_data->server_buffer = malloc(nas_data->server_buffer_size);
415 if (!bytes_per_sample) {
416 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Zero bytes per sample -> nosound\n");
417 return 0;
420 if (!(server = getenv("AUDIOSERVER")) &&
421 !(server = getenv("DISPLAY"))) {
422 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): AUDIOSERVER environment variable not set -> nosound\n");
423 return 0;
426 mp_msg(MSGT_AO, MSGL_V, "ao_nas: init(): Using audioserver %s\n", server);
428 nas_data->aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
429 if (!nas_data->aud) {
430 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't open nas audio server -> nosound\n");
431 return 0;
434 while (channels>1) {
435 nas_data->dev = nas_find_device(nas_data->aud, channels);
436 if (nas_data->dev != AuNone &&
437 ((nas_data->flow = AuCreateFlow(nas_data->aud, NULL)) != 0))
438 break;
439 channels--;
442 if (nas_data->flow == 0) {
443 mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't find a suitable output device -> nosound\n");
444 AuCloseServer(nas_data->aud);
445 nas_data->aud = 0;
446 return 0;
449 AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
450 buffer_size / bytes_per_sample,
451 (buffer_size - NAS_FRAG_SIZE) /
452 bytes_per_sample, 0, NULL);
453 nas_data->gain = AuFixedPointFromFraction(1, 1);
454 AuMakeElementMultiplyConstant(elms+1, 0, nas_data->gain);
455 AuMakeElementExportDevice(elms+2, 1, nas_data->dev, rate,
456 AuUnlimitedSamples, 0, NULL);
457 AuSetElements(nas_data->aud, nas_data->flow, AuTrue, sizeof(elms)/sizeof(*elms), elms, &as);
458 if (as != AuSuccess) {
459 nas_print_error(nas_data->aud, "init(): AuSetElements", as);
460 AuCloseServer(nas_data->aud);
461 nas_data->aud = 0;
462 return 0;
464 AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask |
465 AuEventHandlerTypeMask,
466 AuEventTypeElementNotify, nas_data->flow,
467 nas_event_handler, (AuPointer) nas_data);
468 AuSetErrorHandler(nas_data->aud, nas_error_handler);
469 nas_data->state=AuStateStop;
470 nas_data->expect_underrun=0;
472 pthread_mutex_init(&nas_data->buffer_mutex, NULL);
473 pthread_create(&nas_data->event_thread, NULL, &nas_event_thread_start, nas_data);
475 return 1;
478 // close audio device
479 static void uninit(int immed){
481 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n");
483 nas_data->expect_underrun = 1;
484 while (nas_data->state != AuStateStop) usleep(1000);
485 nas_data->stop_thread = 1;
486 pthread_join(nas_data->event_thread, NULL);
487 AuCloseServer(nas_data->aud);
488 nas_data->aud = 0;
489 free(nas_data->client_buffer);
490 free(nas_data->server_buffer);
493 // stop playing and empty buffers (for seeking/pause)
494 static void reset(){
495 AuStatus as;
497 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: reset()\n");
499 pthread_mutex_lock(&nas_data->buffer_mutex);
500 nas_data->client_buffer_used = 0;
501 pthread_mutex_unlock(&nas_data->buffer_mutex);
502 while (nas_data->state != AuStateStop) {
503 AuStopFlow(nas_data->aud, nas_data->flow, &as);
504 if (as != AuSuccess)
505 nas_print_error(nas_data->aud, "reset(): AuStopFlow", as);
506 usleep(1000);
510 // stop playing, keep buffers (for pause)
511 static void audio_pause()
513 AuStatus as;
514 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_pause()\n");
516 AuStopFlow(nas_data->aud, nas_data->flow, &as);
519 // resume playing, after audio_pause()
520 static void audio_resume()
522 AuStatus as;
524 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n");
526 AuStartFlow(nas_data->aud, nas_data->flow, &as);
527 if (as != AuSuccess)
528 nas_print_error(nas_data->aud,
529 "play(): AuStartFlow", as);
533 // return: how many bytes can be played without blocking
534 static int get_space()
536 int result;
538 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_space()\n");
540 pthread_mutex_lock(&nas_data->buffer_mutex);
541 result = nas_data->client_buffer_size - nas_data->client_buffer_used;
542 pthread_mutex_unlock(&nas_data->buffer_mutex);
544 return result;
547 // plays 'len' bytes of 'data'
548 // it should round it down to outburst*n
549 // return: number of bytes played
550 static int play(void* data,int len,int flags)
552 int maxbursts, playbursts, writelen;
553 AuStatus as;
555 mp_msg(MSGT_AO, MSGL_DBG3,
556 "ao_nas: play(%p, %d, %d)\n",
557 data, len, flags);
559 if (len == 0)
560 return 0;
562 pthread_mutex_lock(&nas_data->buffer_mutex);
563 maxbursts = (nas_data->client_buffer_size -
564 nas_data->client_buffer_used) / ao_data.outburst;
565 playbursts = len / ao_data.outburst;
566 writelen = (playbursts > maxbursts ? maxbursts : playbursts) *
567 ao_data.outburst;
568 pthread_mutex_unlock(&nas_data->buffer_mutex);
570 nas_writeBuffer(nas_data, data, writelen);
572 if (nas_data->state != AuStateStart &&
573 maxbursts == playbursts) {
574 mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play(): Starting flow.\n");
575 nas_data->expect_underrun = 1;
576 AuStartFlow(nas_data->aud, nas_data->flow, &as);
577 if (as != AuSuccess)
578 nas_print_error(nas_data->aud, "play(): AuStartFlow", as);
581 return writelen;
584 // return: delay in seconds between first and last sample in buffer
585 static float get_delay()
587 float result;
589 mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_delay()\n");
591 pthread_mutex_lock(&nas_data->buffer_mutex);
592 result = ((float)(nas_data->client_buffer_used +
593 nas_data->server_buffer_used)) /
594 (float)ao_data.bps;
595 pthread_mutex_unlock(&nas_data->buffer_mutex);
597 return result;