2 * NAS audio output driver
4 * copyright (c) 2001 Tobias Diedrich <ranma@gmx.at>
6 * Based on the libaudiooss parts rewritten by me, which were
7 * originally based on the NAS output plugin for XMMS.
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
14 * This file is part of MPlayer.
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.
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.
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.
32 * Theory of operation:
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)
54 #include <audio/audiolib.h>
59 #include "audio_out.h"
60 #include "audio_out_internal.h"
61 #include "libaf/af_format.h"
63 /* NAS_FRAG_SIZE must be a power-of-two value */
64 #define NAS_FRAG_SIZE 4096
66 static const char * const nas_event_types
[] = {
76 static const char * const nas_elementnotify_kinds
[] = {
83 static const char * const nas_states
[] = {
90 static const char * const nas_reasons
[] = {
100 static const char* nas_reason(unsigned int reason
)
102 if (reason
> 6) reason
= 6;
103 return nas_reasons
[reason
];
106 static const char* nas_elementnotify_kind(unsigned int kind
)
108 if (kind
> 2) kind
= 3;
109 return nas_elementnotify_kinds
[kind
];
112 static const char* nas_event_type(unsigned int type
) {
113 if (type
> 6) type
= 0;
114 return nas_event_types
[type
];
117 static const char* nas_state(unsigned int state
) {
118 if (state
>3) state
= 3;
119 return nas_states
[state
];
122 static const ao_info_t info
=
126 "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
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
;
147 pthread_t event_thread
;
151 static struct ao_nas_data
*nas_data
;
155 static void nas_print_error(AuServer
*aud
, const char *prefix
, AuStatus as
)
158 AuGetErrorText(aud
, as
, s
, 100);
159 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_nas: %s: returned status %d (%s)\n", prefix
, as
, s
);
162 static int nas_readBuffer(struct ao_nas_data
*nas_data
, unsigned int num
)
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",
169 nas_data
->client_buffer_used
, nas_data
->client_buffer_size
,
170 nas_data
->server_buffer_used
, nas_data
->server_buffer_size
);
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
);
177 if (num
> nas_data
->client_buffer_used
)
178 num
= nas_data
->client_buffer_used
;
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.
187 * (Note: Rather than these two buffers, a single circular buffer
188 * could eliminate the memcpy/memmove steps.)
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
);
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
);
201 * Now write the new buffer to the network.
203 AuWriteElement(nas_data
->aud
, nas_data
->flow
, 0, num
, nas_data
->server_buffer
, AuFalse
, &as
);
205 nas_print_error(nas_data
->aud
, "nas_readBuffer(): AuWriteElement", as
);
210 static int nas_writeBuffer(struct ao_nas_data
*nas_data
, void *data
, unsigned int len
)
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
);
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
;
223 pthread_mutex_unlock(&nas_data
->buffer_mutex
);
228 static int nas_empty_event_queue(struct ao_nas_data
*nas_data
)
233 while (AuScanForTypedEvent(nas_data
->aud
, AuEventsQueuedAfterFlush
,
234 AuTrue
, AuEventTypeElementNotify
, &ev
)) {
235 AuDispatchEvent(nas_data
->aud
, &ev
);
241 static void *nas_event_thread_start(void *data
)
243 struct ao_nas_data
*nas_data
= data
;
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
);
251 } while (!nas_data
->stop_thread
);
256 static AuBool
nas_error_handler(AuServer
* aud
, AuErrorEvent
* ev
)
259 AuGetErrorText(aud
, ev
->error_code
, s
, 100);
260 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_nas: error [%s]\n"
272 static AuBool
nas_event_handler(AuServer
*aud
, AuEvent
*ev
, AuEventHandlerRec
*hnd
)
274 AuElementNotifyEvent
*event
= (AuElementNotifyEvent
*) ev
;
275 struct ao_nas_data
*nas_data
= hnd
->data
;
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
);
286 if (event
->num_bytes
> INT_MAX
) {
287 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_nas: num_bytes > 2GB, server buggy?\n");
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
;
294 switch (event
->reason
) {
295 case AuReasonWatermark
:
296 nas_readBuffer(nas_data
, event
->num_bytes
);
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;
305 mp_msg(MSGT_AO
, MSGL_WARN
,
306 "ao_nas: Buffer underrun.\n");
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");
316 if (nas_readBuffer(nas_data
,
317 nas_data
->server_buffer_size
-
318 nas_data
->server_buffer_used
) != 0) {
319 event
->cur_state
= AuStateStart
;
322 mp_msg(MSGT_AO
, MSGL_DBG2
,
323 "ao_nas: Can't refill buffer, stopping flow.\n");
324 AuStopFlow(aud
, nas_data
->flow
, NULL
);
329 nas_data
->state
=event
->cur_state
;
333 static AuDeviceID
nas_find_device(AuServer
*aud
, int nch
)
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
);
346 static unsigned int nas_aformat_to_auformat(unsigned int *format
)
350 return AuFormatLinearUnsigned8
;
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
;
364 *format
=AF_FORMAT_S16_NE
;
365 return nas_aformat_to_auformat(format
);
369 // to set/get/query special features/parameters
370 static int control(int cmd
, void *arg
)
372 AuElementParameters aep
;
374 int retval
= CONTROL_UNKNOWN
;
376 ao_control_vol_t
*vol
= (ao_control_vol_t
*)arg
;
379 case AOCONTROL_GET_VOLUME
:
381 vol
->right
= (float)nas_data
->gain
/AU_FIXED_POINT_SCALE
*50;
382 vol
->left
= vol
->right
;
384 mp_msg(MSGT_AO
, MSGL_DBG2
, "ao_nas: AOCONTROL_GET_VOLUME: %.2f\n", vol
->right
);
388 case AOCONTROL_SET_VOLUME
:
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.
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);
397 aep
.parameters
[AuParmsMultiplyConstantConstant
]=nas_data
->gain
;
398 aep
.flow
= nas_data
->flow
;
400 aep
.num_parameters
= AuParmsMultiplyConstant
;
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
;
414 // open & setup audio device
415 // return: 1=success 0=fail
416 static int init(int rate
,int channels
,int format
,int flags
)
420 unsigned char auformat
= nas_aformat_to_auformat(&format
);
421 int bytes_per_sample
= channels
* AuSizeofFormat(auformat
);
425 (void)flags
; /* shut up 'unused parameter' warning */
427 nas_data
=malloc(sizeof(struct ao_nas_data
));
428 memset(nas_data
, 0, sizeof(struct ao_nas_data
));
430 mp_msg(MSGT_AO
, MSGL_V
, "ao2: %d Hz %d chans %s\n",rate
,channels
,
431 af_fmt2str_short(format
));
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 */
440 * round up to multiple of NAS_FRAG_SIZE
441 * divide by 3 first because of 2:1 split
443 buffer_size
= (buffer_size
/3 + NAS_FRAG_SIZE
-1) & ~(NAS_FRAG_SIZE
-1);
444 ao_data
.buffersize
= buffer_size
*3;
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
);
451 if (!bytes_per_sample
) {
452 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_nas: init(): Zero bytes per sample -> nosound\n");
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");
462 mp_msg(MSGT_AO
, MSGL_V
, "ao_nas: init(): Using audioserver %s\n", server
);
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");
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))
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
);
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
);
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;
508 pthread_mutex_init(&nas_data
->buffer_mutex
, NULL
);
509 pthread_create(&nas_data
->event_thread
, NULL
, &nas_event_thread_start
, nas_data
);
514 // close audio device
515 static void uninit(int immed
){
517 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: uninit()\n");
519 nas_data
->expect_underrun
= 1;
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
);
526 free(nas_data
->client_buffer
);
527 free(nas_data
->server_buffer
);
530 // stop playing and empty buffers (for seeking/pause)
531 static void reset(void){
534 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: reset()\n");
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
);
542 nas_print_error(nas_data
->aud
, "reset(): AuStopFlow", as
);
547 // stop playing, keep buffers (for pause)
548 static void audio_pause(void)
551 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: audio_pause()\n");
553 AuStopFlow(nas_data
->aud
, nas_data
->flow
, &as
);
556 // resume playing, after audio_pause()
557 static void audio_resume(void)
561 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: audio_resume()\n");
563 AuStartFlow(nas_data
->aud
, nas_data
->flow
, &as
);
565 nas_print_error(nas_data
->aud
,
566 "play(): AuStartFlow", as
);
570 // return: how many bytes can be played without blocking
571 static int get_space(void)
575 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: get_space()\n");
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
);
584 // plays 'len' bytes of 'data'
585 // it should round it down to outburst*n
586 // return: number of bytes played
587 static int play(void* data
,int len
,int flags
)
589 int written
, maxbursts
= 0, playbursts
= 0;
592 mp_msg(MSGT_AO
, MSGL_DBG3
,
593 "ao_nas: play(%p, %d, %d)\n",
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
) *
606 pthread_mutex_unlock(&nas_data
->buffer_mutex
);
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
615 written
= nas_writeBuffer(nas_data
, data
, len
);
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
);
624 nas_print_error(nas_data
->aud
, "play(): AuStartFlow", as
);
630 // return: delay in seconds between first and last sample in buffer
631 static float get_delay(void)
635 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: get_delay()\n");
637 pthread_mutex_lock(&nas_data
->buffer_mutex
);
638 result
= ((float)(nas_data
->client_buffer_used
+
639 nas_data
->server_buffer_used
)) /
641 pthread_mutex_unlock(&nas_data
->buffer_mutex
);