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)
35 #include <audio/audiolib.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 char *nas_event_types
[] = {
57 static char *nas_elementnotify_kinds
[] = {
64 static char *nas_states
[] = {
71 static char *nas_reasons
[] = {
81 static char* nas_reason(unsigned int reason
)
83 if (reason
> 6) reason
= 6;
84 return nas_reasons
[reason
];
87 static char* nas_elementnotify_kind(unsigned int kind
)
89 if (kind
> 2) kind
= 3;
90 return nas_elementnotify_kinds
[kind
];
93 static char* nas_event_type(unsigned int type
) {
94 if (type
> 6) type
= 0;
95 return nas_event_types
[type
];
98 static char* nas_state(unsigned int state
) {
99 if (state
>3) state
= 3;
100 return nas_states
[state
];
103 static ao_info_t info
=
107 "Tobias Diedrich <ranma+mplayer@tdiedrich.de>",
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
;
132 static struct ao_nas_data
*nas_data
;
136 static void nas_print_error(AuServer
*aud
, const char *prefix
, AuStatus as
)
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
)
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",
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
);
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
);
186 nas_print_error(nas_data
->aud
, "nas_readBuffer(): AuWriteElement", as
);
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
);
209 static int nas_empty_event_queue(struct ao_nas_data
*nas_data
)
214 while (AuScanForTypedEvent(nas_data
->aud
, AuEventsQueuedAfterFlush
,
215 AuTrue
, AuEventTypeElementNotify
, &ev
)) {
216 AuDispatchEvent(nas_data
->aud
, &ev
);
222 static void *nas_event_thread_start(void *data
)
224 struct ao_nas_data
*nas_data
= data
;
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
);
232 } while (!nas_data
->stop_thread
);
237 static AuBool
nas_error_handler(AuServer
* aud
, AuErrorEvent
* ev
)
240 AuGetErrorText(aud
, ev
->error_code
, s
, 100);
241 mp_msg(MSGT_AO
, MSGL_ERR
, "ao_nas: error [%s]\n"
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
);
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;
286 mp_msg(MSGT_AO
, MSGL_WARN
,
287 "ao_nas: Buffer underrun.\n");
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
;
303 mp_msg(MSGT_AO
, MSGL_DBG2
,
304 "ao_nas: Can't refill buffer, stopping flow.\n");
305 AuStopFlow(aud
, nas_data
->flow
, NULL
);
310 nas_data
->state
=event
->cur_state
;
314 static AuDeviceID
nas_find_device(AuServer
*aud
, int nch
)
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
);
327 static unsigned int nas_aformat_to_auformat(unsigned int *format
)
331 return AuFormatLinearUnsigned8
;
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
;
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
;
355 int retval
= CONTROL_UNKNOWN
;
357 ao_control_vol_t
*vol
= (ao_control_vol_t
*)arg
;
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
);
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
;
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
;
395 // open & setup audio device
396 // return: 1=success 0=fail
397 static int init(int rate
,int channels
,int format
,int flags
)
401 unsigned char auformat
= nas_aformat_to_auformat(&format
);
402 int bytes_per_sample
= channels
* AuSizeofFormat(auformat
);
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");
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");
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");
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))
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
);
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
);
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
);
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;
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
);
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){
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
);
523 nas_print_error(nas_data
->aud
, "reset(): AuStopFlow", as
);
528 // stop playing, keep buffers (for pause)
529 static void audio_pause(void)
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)
542 mp_msg(MSGT_AO
, MSGL_DBG3
, "ao_nas: audio_resume()\n");
544 AuStartFlow(nas_data
->aud
, nas_data
->flow
, &as
);
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)
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
);
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;
573 mp_msg(MSGT_AO
, MSGL_DBG3
,
574 "ao_nas: play(%p, %d, %d)\n",
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
) *
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
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
);
605 nas_print_error(nas_data
->aud
, "play(): AuStartFlow", as
);
611 // return: delay in seconds between first and last sample in buffer
612 static float get_delay(void)
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
)) /
622 pthread_mutex_unlock(&nas_data
->buffer_mutex
);