changeset 7732:328bbac6224c

Fixes: - missing check in init - missing brackets causing failure - nas_aformat_to_auformat not working properly - fix hang that was finally reproducible with high disk activity - don't cut of audio on uninit(), wait for buffer to empty It also simplifies the event_handler, making it more readable and implements Sidik Isani's suggestion to make the buffer size dependent on bytes per second. I've been using it for two days and found no further problems. patch by Tobias Diedrich <td@sim.uni-hannover.de>
author arpi
date Sun, 13 Oct 2002 22:00:15 +0000
parents 1f8961f2b34c
children f81d2bd595a2
files libao2/ao_nas.c
diffstat 1 files changed, 111 insertions(+), 112 deletions(-) [+]
line wrap: on
line diff
--- a/libao2/ao_nas.c	Sun Oct 13 21:58:55 2002 +0000
+++ b/libao2/ao_nas.c	Sun Oct 13 22:00:15 2002 +0000
@@ -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 @@
 	AuFlowID	flow;
 	AuDeviceID	dev;
 
-	int flow_stopped;
-	int flow_paused;
+	unsigned int state;
 	int expect_underrun;
 
 	void *client_buffer;
@@ -178,12 +177,6 @@
 	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 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_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);
+	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;
+	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
+	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 @@
 	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 @@
 {
 	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 @@
 	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 @@
 	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 @@
 		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 @@
 
 // close audio device
 static void uninit(){
-	AuStatus as;
+
+	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: uninit()\n");
 
-	mp_msg(MSGT_AO, MSGL_DBG2, "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 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");
 
-	nas_data->flow_stopped = 0;
-	nas_data->flow_paused = 0;
-	nas_data->expect_underrun = 1;
+	mp_msg(MSGT_AO, MSGL_DBG3, "ao_nas: audio_resume()\n");
+
 	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 @@
 {
 	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 @@
 	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 @@
 
 	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 @@
 {
 	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 +