1 /*
2  * NAS audio output driver
3  *
4  * copyright (c) 2001 Tobias Diedrich <ranma@gmx.at>
5  *
6  * Based on the libaudiooss parts rewritten by me, which were
7  * originally based on the NAS output plugin for XMMS.
8  *
9  * XMMS plugin by Willem Monsuwe
10  * adapted for libaudiooss by Jon Trulson
11  * further modified by Erik Inge Bolsø
12  * largely rewritten and used for this ao driver by Tobias Diedrich
13  *
14  * This file is part of MPlayer.
15  *
16  * MPlayer is free software; you can redistribute it and/or modify
17  * it under the terms of the GNU General Public License as published by
18  * the Free Software Foundation; either version 2 of the License, or
19  * (at your option) any later version.
20  *
21  * MPlayer is distributed in the hope that it will be useful,
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24  * GNU General Public License for more details.
25  *
26  * You should have received a copy of the GNU General Public License along
27  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
28  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29  */
30 
31 /*
32  * Theory of operation:
33  *
34  * The NAS consists of two parts, a server daemon and a client.
35  * We setup the server to use a buffer of size bytes_per_second
36  * with a low watermark of buffer_size - NAS_FRAG_SIZE.
37  * Upon starting the flow the server will generate a buffer underrun
38  * event and the event handler will fill the buffer for the first time.
39  * Now the server will generate a lowwater event when the server buffer
40  * falls below the low watermark value. The event handler gets called
41  * again and refills the buffer by the number of bytes requested by the
42  * server (usually a multiple of 4096). To prevent stuttering on
43  * startup (start of playing, seeks, unpausing) the client buffer should
44  * be bigger than the server buffer. (For debugging we also do some
45  * accounting of what we think how much of the server buffer is filled)
46  */
47 
48 #include <unistd.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <pthread.h>
53 #include <limits.h>
54 #include <audio/audiolib.h>
55 
56 #include "config.h"
57 #include "mp_msg.h"
58 
59 #include "audio_out.h"
60 #include "audio_out_internal.h"
61 #include "libaf/af_format.h"
62 
63 /* NAS_FRAG_SIZE must be a power-of-two value */
64 #define NAS_FRAG_SIZE 4096
65 
66 static const char * const nas_event_types[] = {
67 	"Undefined",
68 	"Undefined",
69 	"ElementNotify",
70 	"GrabNotify",
71 	"MonitorNotify",
72 	"BucketNotify",
73 	"DeviceNotify"
74 };
75 
76 static const char * const nas_elementnotify_kinds[] = {
77 	"LowWater",
78 	"HighWater",
79 	"State",
80 	"Unknown"
81 };
82 
83 static const char * const nas_states[] = {
84 	"Stop",
85 	"Start",
86 	"Pause",
87 	"Any"
88 };
89 
90 static const char * const nas_reasons[] = {
91 	"User",
92 	"Underrun",
93 	"Overrun",
94 	"EOF",
95 	"Watermark",
96 	"Hardware",
97 	"Any"
98 };
99 
nas_reason(unsigned int reason)100 static const char* nas_reason(unsigned int reason)
101 {
102 	if (reason > 6) reason = 6;
103 	return nas_reasons[reason];
104 }
105 
nas_elementnotify_kind(unsigned int kind)106 static const char* nas_elementnotify_kind(unsigned int kind)
107 {
108 	if (kind > 2) kind = 3;
109 	return nas_elementnotify_kinds[kind];
110 }
111 
nas_event_type(unsigned int type)112 static const char* nas_event_type(unsigned int type) {
113 	if (type > 6) type = 0;
114 	return nas_event_types[type];
115 }
116 
nas_state(unsigned int state)117 static const char* nas_state(unsigned int state) {
118 	if (state>3) state = 3;
119 	return nas_states[state];
120 }
121 
122 static const ao_info_t info =
123 {
124 	"NAS audio output",
125 	"nas",
126 	"Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
127 	""
128 };
129 
130 struct ao_nas_data {
131 	AuServer	*aud;
132 	AuFlowID	flow;
133 	AuDeviceID	dev;
134 	AuFixedPoint	gain;
135 
136 	unsigned int state;
137 	int expect_underrun;
138 
139 	char *client_buffer;
140 	char *server_buffer;
141 	unsigned int client_buffer_size;
142 	unsigned int client_buffer_used;
143 	unsigned int server_buffer_size;
144 	unsigned int server_buffer_used;
145 	pthread_mutex_t buffer_mutex;
146 
147 	pthread_t event_thread;
148 	int stop_thread;
149 };
150 
151 static struct ao_nas_data *nas_data;
152 
LIBAO_EXTERN(nas)153 LIBAO_EXTERN(nas)
154 
155 static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as)
156 {
157 	char s[100];
158 	AuGetErrorText(aud, as, s, 100);
159 	mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
160 }
161 
nas_readBuffer(struct ao_nas_data * nas_data,unsigned int num)162 static int nas_readBuffer(struct ao_nas_data *nas_data, unsigned int num)
163 {
164 	AuStatus as;
165 
166 	pthread_mutex_lock(&nas_data->buffer_mutex);
167 	mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
168 			num,
169 			nas_data->client_buffer_used, nas_data->client_buffer_size,
170 			nas_data->server_buffer_used, nas_data->server_buffer_size);
171 
172 	if (nas_data->client_buffer_used == 0) {
173 		mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n");
174 		pthread_mutex_unlock(&nas_data->buffer_mutex);
175 		return 0;
176 	}
177 	if (num > nas_data->client_buffer_used)
178 		num = nas_data->client_buffer_used;
179 
180 	/*
181 	 * It is not appropriate to call AuWriteElement() here because the
182 	 * buffer is locked and delays writing to the network will cause
183 	 * other threads to block waiting for buffer_mutex.  Instead the
184 	 * data is copied to "server_buffer" and written to the network
185 	 * outside of the locked section of code.
186 	 *
187 	 * (Note: Rather than these two buffers, a single circular buffer
188 	 *  could eliminate the memcpy/memmove steps.)
189 	 */
190 	/* make sure we don't overflow the buffer */
191 	if (num > nas_data->server_buffer_size)
192 		num = nas_data->server_buffer_size;
193 	memcpy(nas_data->server_buffer, nas_data->client_buffer, num);
194 
195 	nas_data->client_buffer_used -= num;
196 	nas_data->server_buffer_used += num;
197 	memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
198 	pthread_mutex_unlock(&nas_data->buffer_mutex);
199 
200 	/*
201 	 * Now write the new buffer to the network.
202 	 */
203 	AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as);
204 	if (as != AuSuccess)
205 		nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
206 
207 	return num;
208 }
209 
nas_writeBuffer(struct ao_nas_data * nas_data,void * data,unsigned int len)210 static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, unsigned int len)
211 {
212 	pthread_mutex_lock(&nas_data->buffer_mutex);
213 	mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
214 			len, nas_data->client_buffer_used, nas_data->client_buffer_size,
215 			nas_data->server_buffer_used, nas_data->server_buffer_size);
216 
217 	/* make sure we don't overflow the buffer */
218 	if (len > nas_data->client_buffer_size - nas_data->client_buffer_used)
219 		len = nas_data->client_buffer_size - nas_data->client_buffer_used;
220 	memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
221 	nas_data->client_buffer_used += len;
222 
223 	pthread_mutex_unlock(&nas_data->buffer_mutex);
224 
225 	return len;
226 }
227 
nas_empty_event_queue(struct ao_nas_data * nas_data)228 static int nas_empty_event_queue(struct ao_nas_data *nas_data)
229 {
230 	AuEvent ev;
231 	int result = 0;
232 
233 	while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
234 				   AuTrue, AuEventTypeElementNotify, &ev)) {
235 		AuDispatchEvent(nas_data->aud, &ev);
236 		result = 1;
237 	}
238 	return result;
239 }
240 
nas_event_thread_start(void * data)241 static void *nas_event_thread_start(void *data)
242 {
243 	struct ao_nas_data *nas_data = data;
244 
245 	do {
246 		mp_msg(MSGT_AO, MSGL_DBG2,
247 		       "ao_nas: event thread heartbeat (state=%s)\n",
248 		       nas_state(nas_data->state));
249 		nas_empty_event_queue(nas_data);
250 		usleep(1000);
251 	} while (!nas_data->stop_thread);
252 
253 	return NULL;
254 }
255 
nas_error_handler(AuServer * aud,AuErrorEvent * ev)256 static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
257 {
258 	char s[100];
259 	AuGetErrorText(aud, ev->error_code, s, 100);
260 	mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: error [%s]\n"
261 		"error_code: %d\n"
262 		"request_code: %d\n"
263 		"minor_code: %d\n",
264 		s,
265 		ev->error_code,
266 		ev->request_code,
267 		ev->minor_code);
268 
269 	return AuTrue;
270 }
271 
nas_event_handler(AuServer * aud,AuEvent * ev,AuEventHandlerRec * hnd)272 static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
273 {
274 	AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
275 	struct ao_nas_data *nas_data = hnd->data;
276 
277 	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",
278 		nas_event_type(event->type),
279 		nas_elementnotify_kind(event->kind),
280 		nas_state(event->prev_state),
281 		nas_state(event->cur_state),
282 		nas_reason(event->reason),
283 		(int)event->num_bytes,
284 		nas_data->expect_underrun);
285 
286 	if (event->num_bytes > INT_MAX) {
287 		mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n");
288 	}
289 
290 	if (event->num_bytes > nas_data->server_buffer_used)
291 		event->num_bytes = nas_data->server_buffer_used;
292 	nas_data->server_buffer_used -= event->num_bytes;
293 
294 	switch (event->reason) {
295 	case AuReasonWatermark:
296 		nas_readBuffer(nas_data, event->num_bytes);
297 		break;
298 	case AuReasonUnderrun:
299 		// buffer underrun -> refill buffer
300 		nas_data->server_buffer_used = 0;
301 		if (nas_data->expect_underrun) {
302 			nas_data->expect_underrun = 0;
303 		} else {
304 			static int hint = 1;
305 			mp_msg(MSGT_AO, MSGL_WARN,
306 			       "ao_nas: Buffer underrun.\n");
307 			if (hint) {
308 				hint = 0;
309 				mp_msg(MSGT_AO, MSGL_HINT,
310 				       "Possible reasons are:\n"
311 				       "1) Network congestion.\n"
312 				       "2) Your NAS server is too slow.\n"
313 				       "Try renicing your nasd to e.g. -15.\n");
314 			}
315 		}
316 		if (nas_readBuffer(nas_data,
317 		                   nas_data->server_buffer_size -
318 		                   nas_data->server_buffer_used) != 0) {
319 			event->cur_state = AuStateStart;
320 			break;
321 		}
322 		mp_msg(MSGT_AO, MSGL_DBG2,
323 			"ao_nas: Can't refill buffer, stopping flow.\n");
324 		AuStopFlow(aud, nas_data->flow, NULL);
325 		break;
326 	default:
327 		break;
328 	}
329 	nas_data->state=event->cur_state;
330 	return AuTrue;
331 }
332 
nas_find_device(AuServer * aud,int nch)333 static AuDeviceID nas_find_device(AuServer *aud, int nch)
334 {
335 	int i;
336 	for (i = 0; i < AuServerNumDevices(aud); i++) {
337 		AuDeviceAttributes *dev = AuServerDevice(aud, i);
338 		if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) &&
339 		     AuDeviceNumTracks(dev) == nch) {
340 			return AuDeviceIdentifier(dev);
341 		}
342 	}
343 	return AuNone;
344 }
345 
nas_aformat_to_auformat(unsigned int * format)346 static unsigned int nas_aformat_to_auformat(unsigned int *format)
347 {
348 	switch (*format) {
349 	case	AF_FORMAT_U8:
350 		return AuFormatLinearUnsigned8;
351 	case	AF_FORMAT_S8:
352 		return AuFormatLinearSigned8;
353 	case	AF_FORMAT_U16_LE:
354 		return AuFormatLinearUnsigned16LSB;
355 	case	AF_FORMAT_U16_BE:
356 		return AuFormatLinearUnsigned16MSB;
357 	case	AF_FORMAT_S16_LE:
358 		return AuFormatLinearSigned16LSB;
359 	case	AF_FORMAT_S16_BE:
360 		return AuFormatLinearSigned16MSB;
361 	case	AF_FORMAT_MU_LAW:
362 		return AuFormatULAW8;
363 	default:
364 		*format=AF_FORMAT_S16_NE;
365 		return nas_aformat_to_auformat(format);
366 	}
367 }
368 
369 // to set/get/query special features/parameters
control(int cmd,void * arg)370 static int control(int cmd, void *arg)
371 {
372 	AuElementParameters aep;
373 	AuStatus as;
374 	int retval = CONTROL_UNKNOWN;
375 
376 	ao_control_vol_t *vol = (ao_control_vol_t *)arg;
377 
378 	switch (cmd) {
379 	case AOCONTROL_GET_VOLUME:
380 
381 		vol->right = (float)nas_data->gain/AU_FIXED_POINT_SCALE*50;
382 		vol->left = vol->right;
383 
384 		mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_GET_VOLUME: %.2f\n", vol->right);
385 		retval = CONTROL_OK;
386 		break;
387 
388 	case AOCONTROL_SET_VOLUME:
389 		/*
390 		 * kn: we should have vol->left == vol->right but i don't
391 		 * know if something can change it outside of ao_nas
392 		 * so i take the mean of both values.
393 		 */
394 		nas_data->gain = AU_FIXED_POINT_SCALE*((vol->left+vol->right)/2)/50;
395 		mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: AOCONTROL_SET_VOLUME: %.2f\n", (vol->left+vol->right)/2);
396 
397 		aep.parameters[AuParmsMultiplyConstantConstant]=nas_data->gain;
398 		aep.flow = nas_data->flow;
399 		aep.element_num = 1;
400 		aep.num_parameters = AuParmsMultiplyConstant;
401 
402 		AuSetElementParameters(nas_data->aud, 1, &aep, &as);
403 		if (as != AuSuccess) {
404 			nas_print_error(nas_data->aud,
405 			                "control(): AuSetElementParameters", as);
406 			retval = CONTROL_ERROR;
407 		} else retval = CONTROL_OK;
408 		break;
409 	};
410 
411 	return retval;
412 }
413 
414 // open & setup audio device
415 // return: 1=success 0=fail
init(int rate,int channels,int format,int flags)416 static int init(int rate,int channels,int format,int flags)
417 {
418 	AuElement elms[3];
419 	AuStatus as;
420 	unsigned char auformat = nas_aformat_to_auformat(&format);
421 	int bytes_per_sample = channels * AuSizeofFormat(auformat);
422 	int buffer_size;
423 	char *server;
424 
425 	(void)flags; /* shut up 'unused parameter' warning */
426 
427 	nas_data=malloc(sizeof(struct ao_nas_data));
428 	memset(nas_data, 0, sizeof(struct ao_nas_data));
429 
430 	mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz  %d chans  %s\n",rate,channels,
431 		af_fmt2str_short(format));
432 
433 	ao_data.format = format;
434 	ao_data.samplerate = rate;
435 	ao_data.channels = channels;
436 	ao_data.outburst = NAS_FRAG_SIZE;
437 	ao_data.bps = rate * bytes_per_sample;
438 	buffer_size = ao_data.bps; /* buffer 1 second */
439 	/*
440 	 * round up to multiple of NAS_FRAG_SIZE
441 	 * divide by 3 first because of 2:1 split
442 	 */
443 	buffer_size = (buffer_size/3 + NAS_FRAG_SIZE-1) & ~(NAS_FRAG_SIZE-1);
444 	ao_data.buffersize = buffer_size*3;
445 
446 	nas_data->client_buffer_size = buffer_size*2;
447 	nas_data->client_buffer = malloc(nas_data->client_buffer_size);
448 	nas_data->server_buffer_size = buffer_size;
449 	nas_data->server_buffer = malloc(nas_data->server_buffer_size);
450 
451 	if (!bytes_per_sample) {
452 		mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Zero bytes per sample -> nosound\n");
453 		return 0;
454 	}
455 
456 	if (!(server = getenv("AUDIOSERVER")) &&
457 	    !(server = getenv("DISPLAY"))) {
458 		mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): AUDIOSERVER environment variable not set -> nosound\n");
459 		return 0;
460 	}
461 
462 	mp_msg(MSGT_AO, MSGL_V, "ao_nas: init(): Using audioserver %s\n", server);
463 
464 	nas_data->aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
465 	if (!nas_data->aud) {
466 		mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't open nas audio server -> nosound\n");
467 		return 0;
468 	}
469 
470 	while (channels>0) {
471 		nas_data->dev = nas_find_device(nas_data->aud, channels);
472 		if (nas_data->dev != AuNone &&
473 		    ((nas_data->flow = AuCreateFlow(nas_data->aud, NULL)) != 0))
474 			break;
475 		channels--;
476 	}
477 
478 	if (nas_data->flow == 0) {
479 		mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Can't find a suitable output device -> nosound\n");
480 		AuCloseServer(nas_data->aud);
481 		nas_data->aud = 0;
482 		return 0;
483 	}
484 
485 	AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
486 				buffer_size / bytes_per_sample,
487 				(buffer_size - NAS_FRAG_SIZE) /
488 				bytes_per_sample, 0, NULL);
489 	nas_data->gain = AuFixedPointFromFraction(1, 1);
490 	AuMakeElementMultiplyConstant(elms+1, 0, nas_data->gain);
491 	AuMakeElementExportDevice(elms+2, 1, nas_data->dev, rate,
492 				AuUnlimitedSamples, 0, NULL);
493 	AuSetElements(nas_data->aud, nas_data->flow, AuTrue, sizeof(elms)/sizeof(*elms), elms, &as);
494 	if (as != AuSuccess) {
495 		nas_print_error(nas_data->aud, "init(): AuSetElements", as);
496 		AuCloseServer(nas_data->aud);
497 		nas_data->aud = 0;
498 		return 0;
499 	}
500 	AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask |
501 				AuEventHandlerTypeMask,
502 				AuEventTypeElementNotify, nas_data->flow,
503 				nas_event_handler, (AuPointer) nas_data);
504 	AuSetErrorHandler(nas_data->aud, nas_error_handler);
505 	nas_data->state=AuStateStop;
506 	nas_data->expect_underrun=0;
507 
508 	pthread_mutex_init(&nas_data->buffer_mutex, NULL);
509 	pthread_create(&nas_data->event_thread, NULL, &nas_event_thread_start, nas_data);
510 
511 	return 1;
512 }
513 
514 // close audio device
uninit(int immed)515 static void uninit(int immed){
516 
517 	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n");
518 
519 	nas_data->expect_underrun = 1;
520 	if (!immed)
521 	while (nas_data->state != AuStateStop) usleep(1000);
522 	nas_data->stop_thread = 1;
523 	pthread_join(nas_data->event_thread, NULL);
524 	AuCloseServer(nas_data->aud);
525 	nas_data->aud = 0;
526 	free(nas_data->client_buffer);
527 	free(nas_data->server_buffer);
528 }
529 
530 // stop playing and empty buffers (for seeking/pause)
reset(void)531 static void reset(void){
532 	AuStatus as;
533 
534 	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: reset()\n");
535 
536 	pthread_mutex_lock(&nas_data->buffer_mutex);
537 	nas_data->client_buffer_used = 0;
538 	pthread_mutex_unlock(&nas_data->buffer_mutex);
539 	while (nas_data->state != AuStateStop) {
540 		AuStopFlow(nas_data->aud, nas_data->flow, &as);
541 		if (as != AuSuccess)
542 			nas_print_error(nas_data->aud, "reset(): AuStopFlow", as);
543 		usleep(1000);
544 	}
545 }
546 
547 // stop playing, keep buffers (for pause)
audio_pause(void)548 static void audio_pause(void)
549 {
550 	AuStatus as;
551 	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_pause()\n");
552 
553 	AuStopFlow(nas_data->aud, nas_data->flow, &as);
554 }
555 
556 // resume playing, after audio_pause()
audio_resume(void)557 static void audio_resume(void)
558 {
559 	AuStatus as;
560 
561 	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n");
562 
563 	AuStartFlow(nas_data->aud, nas_data->flow, &as);
564 	if (as != AuSuccess)
565 		nas_print_error(nas_data->aud,
566 		                "play(): AuStartFlow", as);
567 }
568 
569 
570 // return: how many bytes can be played without blocking
get_space(void)571 static int get_space(void)
572 {
573 	int result;
574 
575 	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_space()\n");
576 
577 	pthread_mutex_lock(&nas_data->buffer_mutex);
578 	result = nas_data->client_buffer_size - nas_data->client_buffer_used;
579 	pthread_mutex_unlock(&nas_data->buffer_mutex);
580 
581 	return result;
582 }
583 
584 // plays 'len' bytes of 'data'
585 // it should round it down to outburst*n
586 // return: number of bytes played
play(void * data,int len,int flags)587 static int play(void* data,int len,int flags)
588 {
589 	int written, maxbursts = 0, playbursts = 0;
590 	AuStatus as;
591 
592 	mp_msg(MSGT_AO, MSGL_DBG3,
593 	       "ao_nas: play(%p, %d, %d)\n",
594 	       data, len, flags);
595 
596 	if (len == 0)
597 		return 0;
598 
599 	if (!(flags & AOPLAY_FINAL_CHUNK)) {
600 		pthread_mutex_lock(&nas_data->buffer_mutex);
601 		maxbursts = (nas_data->client_buffer_size -
602 			     nas_data->client_buffer_used) / ao_data.outburst;
603 		playbursts = len / ao_data.outburst;
604 		len = (playbursts > maxbursts ? maxbursts : playbursts) *
605 			   ao_data.outburst;
606 		pthread_mutex_unlock(&nas_data->buffer_mutex);
607 	}
608 
609 	/*
610 	 * If AOPLAY_FINAL_CHUNK is set, we did not actually check len fits
611 	 * into the available buffer space, but mplayer.c shouldn't give us
612 	 * more to play than we report to it by get_space(), so this should be
613 	 * fine.
614 	 */
615 	written = nas_writeBuffer(nas_data, data, len);
616 
617 	if (nas_data->state != AuStateStart &&
618 	    (maxbursts == playbursts ||
619 	     flags & AOPLAY_FINAL_CHUNK)) {
620 		mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play(): Starting flow.\n");
621 		nas_data->expect_underrun = 1;
622 		AuStartFlow(nas_data->aud, nas_data->flow, &as);
623 		if (as != AuSuccess)
624 			nas_print_error(nas_data->aud, "play(): AuStartFlow", as);
625 	}
626 
627 	return written;
628 }
629 
630 // return: delay in seconds between first and last sample in buffer
get_delay(void)631 static float get_delay(void)
632 {
633 	float result;
634 
635 	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_delay()\n");
636 
637 	pthread_mutex_lock(&nas_data->buffer_mutex);
638 	result = ((float)(nas_data->client_buffer_used +
639 			  nas_data->server_buffer_used)) /
640 		 (float)ao_data.bps;
641 	pthread_mutex_unlock(&nas_data->buffer_mutex);
642 
643 	return result;
644 }
645