diff options
Diffstat (limited to 'libao2')
-rw-r--r-- | libao2/ao_nas.c | 223 |
1 files changed, 111 insertions, 112 deletions
diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c index bd299f3793..1b22f1254b 100644 --- a/libao2/ao_nas.c +++ b/libao2/ao_nas.c @@ -13,8 +13,8 @@ * Theory of operation: * * The NAS consists of two parts, a server daemon and a client. - * We setup the server to use a buffer of size NAS_BUFFER_SIZE - * with a low watermark of NAS_BUFFER_SIZE - NAS_FRAG_SIZE. + * We setup the server to use a buffer of size bytes_per_second + * with a low watermark of buffer_size - NAS_FRAG_SIZE. * Upon starting the flow the server will generate a buffer underrun * event and the event handler will fill the buffer for the first time. * Now the server will generate a lowwater event when the server buffer @@ -26,8 +26,10 @@ * accounting of what we think how much of the server buffer is filled) */ +#include <unistd.h> #include <stdio.h> #include <stdlib.h> +#include <string.h> #include <pthread.h> #include <audio/audiolib.h> @@ -38,8 +40,6 @@ #include "afmt.h" #define NAS_FRAG_SIZE 4096 -#define NAS_FRAG_COUNT 8 -#define NAS_BUFFER_SIZE NAS_FRAG_SIZE * NAS_FRAG_COUNT static char *nas_event_types[] = { "Undefined", @@ -110,8 +110,7 @@ struct ao_nas_data { AuFlowID flow; AuDeviceID dev; - int flow_stopped; - int flow_paused; + unsigned int state; int expect_underrun; void *client_buffer; @@ -178,12 +177,6 @@ static int nas_readBuffer(struct ao_nas_data *nas_data, int num) AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->server_buffer, AuFalse, &as); if (as != AuSuccess) nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as); - - if (nas_data->flow_paused) { - AuPauseFlow(nas_data->aud, nas_data->flow, &as); - if (as != AuSuccess) - nas_print_error(nas_data->aud, "nas_readBuffer(): AuPauseFlow", as); - } return num; } @@ -217,13 +210,16 @@ static int nas_empty_event_queue(struct ao_nas_data *nas_data) static void *nas_event_thread_start(void *data) { struct ao_nas_data *nas_data = data; - AuEvent ev; - AuBool result; do { + mp_msg(MSGT_AO, MSGL_DBG2, + "ao_nas: event thread heartbeat (state=%s)\n", + nas_state(nas_data->state)); nas_empty_event_queue(nas_data); - usleep(10000); + usleep(1000); } while (!nas_data->stop_thread); + + return NULL; } static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev) @@ -245,56 +241,53 @@ static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev) static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd) { AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev; - AuStatus as; struct ao_nas_data *nas_data = hnd->data; - switch (ev->type) { - case AuEventTypeElementNotify: - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: event_handler(): kind %s state %s->%s reason %s numbytes %d expect_underrun %d\n", - nas_elementnotify_kind(event->kind), - nas_state(event->prev_state), - nas_state(event->cur_state), - nas_reason(event->reason), - event->num_bytes, - nas_data->expect_underrun); - - nas_data->server_buffer_used -= event->num_bytes; - if (nas_data->server_buffer_used < 0) - nas_data->server_buffer_used = 0; - - switch (event->kind) { - case AuElementNotifyKindLowWater: - nas_readBuffer(nas_data, event->num_bytes); - break; - case AuElementNotifyKindState: - if (event->cur_state == AuStatePause) { - switch (event->reason) { - case AuReasonUnderrun: - // buffer underrun -> refill buffer - if (nas_data->expect_underrun) - nas_data->expect_underrun = 0; - else { - mp_msg(MSGT_AO, MSGL_WARN, "ao_nas: Buffer underrun.\n"); - mp_msg(MSGT_AO, MSGL_HINT, "Possible reasons are network congestion or your NAS server is too slow.\n" - "Try renicing your nasd to e.g. -15.\n"); - } - nas_data->server_buffer_used = 0; - if (nas_readBuffer(nas_data, nas_data->server_buffer_size - nas_data->server_buffer_used) == 0) - nas_data->flow_stopped = 1; - break; - default: - break; - } - } - break; - default: // silently ignored + 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", + nas_event_type(event->type), + nas_elementnotify_kind(event->kind), + nas_state(event->prev_state), + nas_state(event->cur_state), + nas_reason(event->reason), + event->num_bytes, + nas_data->expect_underrun); + + nas_data->server_buffer_used -= event->num_bytes; + if (nas_data->server_buffer_used < 0) + nas_data->server_buffer_used = 0; + + switch (event->reason) { + case AuReasonWatermark: + nas_readBuffer(nas_data, event->num_bytes); + break; + case AuReasonUnderrun: + // buffer underrun -> refill buffer + nas_data->server_buffer_used = 0; + if (nas_data->expect_underrun) { + nas_data->expect_underrun = 0; + } else { + mp_msg(MSGT_AO, MSGL_WARN, + "ao_nas: Buffer underrun.\n"); + mp_msg(MSGT_AO, MSGL_HINT, + "Possible reasons are:" + "1) Network congestion." + "2) Your NAS server is too slow." + "Try renicing your nasd to e.g. -15.\n"); + } + if (nas_readBuffer(nas_data, + nas_data->server_buffer_size - + nas_data->server_buffer_used) != 0) { + event->cur_state = AuStateStart; break; } + mp_msg(MSGT_AO, MSGL_DBG2, + "ao_nas: Can't refill buffer, stopping flow.\n"); + AuStopFlow(nas_data->aud, nas_data->flow, NULL); break; - default: - mp_msg(MSGT_AO, MSGL_WARN, "ao_nas: nas_event_handler(): unhandled event type %d\n", ev->type); + default: break; } + nas_data->state=event->cur_state; return AuTrue; } @@ -311,30 +304,31 @@ static AuDeviceID nas_find_device(AuServer *aud, int nch) return AuNone; } -static unsigned int nas_aformat_to_auformat(unsigned int format) +static unsigned int nas_aformat_to_auformat(unsigned int *format) { - unsigned int res=format << 8; - switch (format) { + switch (*format) { case AFMT_U8: - return res + AuFormatLinearUnsigned8; + return AuFormatLinearUnsigned8; case AFMT_S8: - return res + AuFormatLinearSigned8; + return AuFormatLinearSigned8; case AFMT_U16_LE: - return res + AuFormatLinearUnsigned16LSB; + return AuFormatLinearUnsigned16LSB; case AFMT_U16_BE: - return res + AuFormatLinearUnsigned16MSB; + return AuFormatLinearUnsigned16MSB; #ifndef WORDS_BIGENDIAN default: + *format=AFMT_S16_LE; #endif case AFMT_S16_LE: - return (AFMT_S16_LE << 8) + AuFormatLinearSigned16LSB; + return AuFormatLinearSigned16LSB; #ifdef WORDS_BIGENDIAN default: + *format=AFMT_S16_BE; #endif case AFMT_S16_BE: - return (AFMT_S16_BE << 8) + AuFormatLinearSigned16MSB; + return AuFormatLinearSigned16MSB; case AFMT_MU_LAW: - return res + AuFormatULAW8; + return AuFormatULAW8; } } @@ -349,8 +343,9 @@ static int init(int rate,int channels,int format,int flags) { AuElement elms[3]; AuStatus as; - unsigned char auformat = nas_aformat_to_auformat(format); + unsigned char auformat = nas_aformat_to_auformat(&format); int bytes_per_sample = channels * AuSizeofFormat(auformat); + int buffer_size; char *server; nas_data=malloc(sizeof(struct ao_nas_data)); @@ -359,17 +354,23 @@ static int init(int rate,int channels,int format,int flags) mp_msg(MSGT_AO, MSGL_V, "ao2: %d Hz %d chans %s\n",rate,channels, audio_out_format_name(format)); - nas_data->client_buffer_size = NAS_BUFFER_SIZE*2; - nas_data->client_buffer = malloc(nas_data->client_buffer_size); - nas_data->server_buffer_size = NAS_BUFFER_SIZE; - nas_data->server_buffer = malloc(nas_data->server_buffer_size); - - ao_data.format = auformat >> 8; + ao_data.format = format; ao_data.samplerate = rate; ao_data.channels = channels; - ao_data.buffersize = NAS_BUFFER_SIZE * 2; ao_data.outburst = NAS_FRAG_SIZE; ao_data.bps = rate * bytes_per_sample; + buffer_size = ao_data.bps; /* buffer 1 second */ + /* + * round up to multiple of NAS_FRAG_SIZE + * divide by 3 first because of 2:1 split + */ + buffer_size = (buffer_size/3 + NAS_FRAG_SIZE-1) & ~(NAS_FRAG_SIZE-1); + ao_data.buffersize = buffer_size*3; + + nas_data->client_buffer_size = buffer_size*2; + nas_data->client_buffer = malloc(nas_data->client_buffer_size); + nas_data->server_buffer_size = buffer_size; + nas_data->server_buffer = malloc(nas_data->server_buffer_size); if (!bytes_per_sample) { mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: init(): Zero bytes per sample -> nosound\n"); @@ -393,7 +394,7 @@ static int init(int rate,int channels,int format,int flags) while (channels>1) { nas_data->dev = nas_find_device(nas_data->aud, channels); if (nas_data->dev != AuNone && - (nas_data->flow = AuCreateFlow(nas_data->aud, NULL) != 0)) + ((nas_data->flow = AuCreateFlow(nas_data->aud, NULL)) != 0)) break; channels--; } @@ -405,21 +406,25 @@ static int init(int rate,int channels,int format,int flags) return 0; } - AuMakeElementImportClient(elms, rate, auformat & 0xff, channels, AuTrue, - NAS_BUFFER_SIZE / bytes_per_sample, - (NAS_BUFFER_SIZE - NAS_FRAG_SIZE) / bytes_per_sample, - 0, NULL); + AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue, + buffer_size / bytes_per_sample, + (buffer_size - NAS_FRAG_SIZE) / + bytes_per_sample, 0, NULL); AuMakeElementExportDevice(elms+1, 0, nas_data->dev, rate, AuUnlimitedSamples, 0, NULL); AuSetElements(nas_data->aud, nas_data->flow, AuTrue, 2, elms, &as); - if (as != AuSuccess) + if (as != AuSuccess) { nas_print_error(nas_data->aud, "init(): AuSetElements", as); + AuCloseServer(nas_data->aud); + nas_data->aud = 0; + return 0; + } AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask | AuEventHandlerTypeMask, AuEventTypeElementNotify, nas_data->flow, nas_event_handler, (AuPointer) nas_data); AuSetErrorHandler(nas_data->aud, nas_error_handler); - nas_data->flow_stopped=1; + nas_data->state=AuStateStop; nas_data->expect_underrun=0; pthread_mutex_init(&nas_data->buffer_mutex, NULL); @@ -430,17 +435,13 @@ static int init(int rate,int channels,int format,int flags) // close audio device static void uninit(){ - AuStatus as; - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: uninit()\n"); + mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n"); + nas_data->expect_underrun = 1; + while (nas_data->state != AuStateStop) usleep(1000); nas_data->stop_thread = 1; pthread_join(nas_data->event_thread, NULL); - if (!nas_data->flow_stopped) { - AuStopFlow(nas_data->aud, nas_data->flow, &as); - if (as != AuSuccess) - nas_print_error(nas_data->aud, "uninit(): AuStopFlow", as); - } AuCloseServer(nas_data->aud); nas_data->aud = 0; free(nas_data->client_buffer); @@ -451,45 +452,39 @@ static void uninit(){ static void reset(){ AuStatus as; - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: reset()\n"); + mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: reset()\n"); pthread_mutex_lock(&nas_data->buffer_mutex); nas_data->client_buffer_used = 0; - if (!nas_data->flow_stopped) { + pthread_mutex_unlock(&nas_data->buffer_mutex); + while (nas_data->state != AuStateStop) { AuStopFlow(nas_data->aud, nas_data->flow, &as); if (as != AuSuccess) nas_print_error(nas_data->aud, "reset(): AuStopFlow", as); - nas_data->flow_stopped = 1; + usleep(1000); } - nas_data->server_buffer_used = 0; - AuSync(nas_data->aud, AuTrue); - pthread_mutex_unlock(&nas_data->buffer_mutex); } // stop playing, keep buffers (for pause) static void audio_pause() { AuStatus as; + mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_pause()\n"); - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: audio_pause()\n"); - - nas_data->flow_paused = 1; + AuStopFlow(nas_data->aud, nas_data->flow, &as); } // resume playing, after audio_pause() static void audio_resume() { AuStatus as; - AuEvent ev; - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: audio_resume()\n"); + mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n"); - nas_data->flow_stopped = 0; - nas_data->flow_paused = 0; - nas_data->expect_underrun = 1; AuStartFlow(nas_data->aud, nas_data->flow, &as); if (as != AuSuccess) - nas_print_error(nas_data->aud, "play(): AuStartFlow", as); + nas_print_error(nas_data->aud, + "play(): AuStartFlow", as); } @@ -498,7 +493,7 @@ static int get_space() { int result; - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: get_space()\n"); + mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_space()\n"); pthread_mutex_lock(&nas_data->buffer_mutex); result = nas_data->client_buffer_size - nas_data->client_buffer_used; @@ -515,7 +510,12 @@ static int play(void* data,int len,int flags) int maxbursts, playbursts, writelen; AuStatus as; - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play()\n"); + mp_msg(MSGT_AO, MSGL_DBG3, + "ao_nas: play(%p, %d, %d)\n", + data, len, flags); + + if (len == 0) + return 0; pthread_mutex_lock(&nas_data->buffer_mutex); maxbursts = (nas_data->client_buffer_size - @@ -527,14 +527,13 @@ static int play(void* data,int len,int flags) nas_writeBuffer(nas_data, data, writelen); - if (nas_data->flow_stopped) { - AuEvent ev; - + if (nas_data->state != AuStateStart && + maxbursts == playbursts) { + mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: play(): Starting flow.\n"); nas_data->expect_underrun = 1; AuStartFlow(nas_data->aud, nas_data->flow, &as); if (as != AuSuccess) nas_print_error(nas_data->aud, "play(): AuStartFlow", as); - nas_data->flow_stopped = 0; } return writelen; @@ -545,7 +544,7 @@ static float get_delay() { float result; - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: get_delay()\n"); + mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: get_delay()\n"); pthread_mutex_lock(&nas_data->buffer_mutex); result = ((float)(nas_data->client_buffer_used + |