summaryrefslogtreecommitdiffstats
path: root/libao2/ao_nas.c
diff options
context:
space:
mode:
authoratmos4 <atmos4@b3059339-0415-0410-9bf9-f77b7e298cf2>2002-02-20 22:45:48 +0000
committeratmos4 <atmos4@b3059339-0415-0410-9bf9-f77b7e298cf2>2002-02-20 22:45:48 +0000
commitd0bfffd1a2526613d5c1fa07570a4b14f46c17c8 (patch)
treee5970594001e5f2fded9514b7320f3fdf48057d6 /libao2/ao_nas.c
parent14d11ff0fc40fdfa705ab9348fcd99eba0c245ef (diff)
downloadmpv-d0bfffd1a2526613d5c1fa07570a4b14f46c17c8.tar.bz2
mpv-d0bfffd1a2526613d5c1fa07570a4b14f46c17c8.tar.xz
improved event handling, implemented working pause that does not flush all buffers, work around a deadlock in the new threadsafe version 1.5 of libaudio by Tobias Diedrich
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@4776 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'libao2/ao_nas.c')
-rw-r--r--libao2/ao_nas.c444
1 files changed, 300 insertions, 144 deletions
diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c
index 54581edad1..7a651324f7 100644
--- a/libao2/ao_nas.c
+++ b/libao2/ao_nas.c
@@ -21,13 +21,70 @@
#include "audio_out_internal.h"
#include "afmt.h"
-#define FRAG_SIZE 4096
-#define FRAG_COUNT 8
-#define BUFFER_SIZE FRAG_SIZE * FRAG_COUNT
+#define NAS_FRAG_SIZE 4096
+#define NAS_FRAG_COUNT 8
+#define NAS_BUFFER_SIZE NAS_FRAG_SIZE * NAS_FRAG_COUNT
#define NAS_DEBUG 0
#if NAS_DEBUG == 1
+
+static char *nas_event_types[] = {
+ "Undefined",
+ "Undefined",
+ "ElementNotify",
+ "GrabNotify",
+ "MonitorNotify",
+ "BucketNotify",
+ "DeviceNotify"
+};
+
+static char *nas_elementnotify_kinds[] = {
+ "LowWater",
+ "HighWater",
+ "State",
+ "Unknown"
+};
+
+static char *nas_states[] = {
+ "Stop",
+ "Start",
+ "Pause",
+ "Any"
+};
+
+static char *nas_reasons[] = {
+ "User",
+ "Underrun",
+ "Overrun",
+ "EOF",
+ "Watermark",
+ "Hardware",
+ "Any"
+};
+
+static char* nas_reason(unsigned int reason)
+{
+ if (reason > 6) reason = 6;
+ return nas_reasons[reason];
+}
+
+static char* nas_elementnotify_kind(unsigned int kind)
+{
+ if (kind > 2) kind = 3;
+ return nas_elementnotify_kinds[kind];
+}
+
+static char* nas_event_type(unsigned int type) {
+ if (type > 6) type = 0;
+ return nas_event_types[type];
+}
+
+static char* nas_state(unsigned int state) {
+ if (state>3) state = 3;
+ return nas_states[state];
+}
+
#define DPRINTF(format, args...) fprintf(stderr, format, ## args); \
fflush(stderr)
#else
@@ -42,109 +99,115 @@ static ao_info_t info =
""
};
-static AuServer* aud;
-static AuFlowID flow;
-static AuDeviceID dev;
+struct ao_nas_data {
+ AuServer *aud;
+ AuFlowID flow;
+ AuDeviceID dev;
+
+ int flow_stopped;
+ int flow_paused;
-static void *client_buffer;
-static int client_buffer_size = BUFFER_SIZE;
-static int client_buffer_used;
-static int server_buffer_size = BUFFER_SIZE;
-static int server_buffer_used;
-static pthread_mutex_t buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
+ void *client_buffer;
+ int client_buffer_size;
+ int client_buffer_used;
+ int server_buffer_size;
+ int server_buffer_used;
+ pthread_mutex_t buffer_mutex;
+
+ pthread_t event_thread;
+ int stop_thread;
+};
-pthread_t event_thread;
-static int stop_thread;
+static struct ao_nas_data *nas_data;
LIBAO_EXTERN(nas)
-static void wait_for_event()
+static void nas_print_error(AuServer *aud, char *prefix, AuStatus as)
{
- AuEvent ev;
-
- AuNextEvent(aud, AuTrue, &ev);
- AuDispatchEvent(aud, &ev);
+ char s[100];
+ AuGetErrorText(aud, as, s, 100);
+ fprintf(stderr, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s);
+ fflush(stderr);
}
-static int readBuffer(int num)
+static int nas_readBuffer(struct ao_nas_data *nas_data, int num)
{
- pthread_mutex_lock(&buffer_mutex);
- DPRINTF("readBuffer(): num=%d client=%d/%d server=%d/%d\n",
+ AuStatus as;
+
+ pthread_mutex_lock(&nas_data->buffer_mutex);
+ DPRINTF("ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n",
num,
- client_buffer_used, client_buffer_size,
- server_buffer_used, server_buffer_size);
+ nas_data->client_buffer_used, nas_data->client_buffer_size,
+ nas_data->server_buffer_used, nas_data->server_buffer_size);
- if (client_buffer_used == 0) {
- DPRINTF("buffer is empty, nothing read.\n");
- pthread_mutex_unlock(&buffer_mutex);
+ if (nas_data->client_buffer_used == 0) {
+ DPRINTF("ao_nas: buffer is empty, nothing read.\n");
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
return 0;
}
- if (client_buffer_used < num)
- num = client_buffer_used;
-
- AuWriteElement(aud, flow, 0, num, client_buffer, AuFalse, NULL);
- client_buffer_used -= num;
- server_buffer_used += num;
- memmove(client_buffer, client_buffer + num, client_buffer_used);
- pthread_mutex_unlock(&buffer_mutex);
+ if (nas_data->client_buffer_used < num)
+ num = nas_data->client_buffer_used;
+
+ AuWriteElement(nas_data->aud, nas_data->flow, 0, num, nas_data->client_buffer, AuFalse, &as);
+ if (as != AuSuccess)
+ nas_print_error(nas_data->aud, "nas_readBuffer(): AuWriteElement", as);
+ else {
+ nas_data->client_buffer_used -= num;
+ nas_data->server_buffer_used += num;
+ memmove(nas_data->client_buffer, nas_data->client_buffer + num, nas_data->client_buffer_used);
+ }
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
+
+ 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;
}
-static void writeBuffer(void *data, int len)
+static void nas_writeBuffer(struct ao_nas_data *nas_data, void *data, int len)
{
- pthread_mutex_lock(&buffer_mutex);
- DPRINTF("writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
- len, client_buffer_used, client_buffer_size,
- server_buffer_used, server_buffer_size);
+ pthread_mutex_lock(&nas_data->buffer_mutex);
+ DPRINTF("ao_nas: nas_writeBuffer(): len=%d client=%d/%d server=%d/%d\n",
+ len, nas_data->client_buffer_used, nas_data->client_buffer_size,
+ nas_data->server_buffer_used, nas_data->server_buffer_size);
- memcpy(client_buffer + client_buffer_used, data, len);
- client_buffer_used += len;
+ memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len);
+ nas_data->client_buffer_used += len;
- pthread_mutex_unlock(&buffer_mutex);
- if (server_buffer_used < server_buffer_size)
- readBuffer(server_buffer_size - server_buffer_used);
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
+ if (nas_data->server_buffer_used < nas_data->server_buffer_size)
+ nas_readBuffer(nas_data, nas_data->server_buffer_size - nas_data->server_buffer_used);
}
-
-static void *event_thread_start(void *data)
+static int nas_empty_event_queue(struct ao_nas_data *nas_data)
{
- while (!stop_thread) {
- wait_for_event();
+ AuEvent ev;
+ int result = 0;
+
+ while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush,
+ AuTrue, AuEventTypeElementNotify, &ev)) {
+ AuDispatchEvent(nas_data->aud, &ev);
+ result = 1;
}
+ return result;
}
-static AuBool event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd)
+static void *nas_event_thread_start(void *data)
{
- switch (ev->type) {
- case AuEventTypeElementNotify: {
- AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev;
- DPRINTF("event_handler(): kind %d state %d->%d reason %d numbytes %d\n",
- event->kind,
- event->prev_state,
- event->cur_state,
- event->reason,
- event->num_bytes);
+ struct ao_nas_data *nas_data = data;
+ AuEvent ev;
+ AuBool result;
- switch (event->kind) {
- case AuElementNotifyKindLowWater:
- server_buffer_used -= event->num_bytes;
- readBuffer(event->num_bytes);
- break;
- case AuElementNotifyKindState:
- if ((event->cur_state == AuStatePause) &&
- (event->reason != AuReasonUser)) {
- // buffer underrun -> refill buffer
- server_buffer_used = 0;
- readBuffer(server_buffer_size - server_buffer_used);
- }
- }
- }
- }
- return AuTrue;
+ do {
+ nas_empty_event_queue(nas_data);
+ usleep(10000);
+ } while (!nas_data->stop_thread);
}
-static AuBool error_handler(AuServer* aud, AuErrorEvent* ev)
+static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev)
{
char s[100];
AuGetErrorText(aud, ev->error_code, s, 100);
@@ -161,7 +224,54 @@ static AuBool error_handler(AuServer* aud, AuErrorEvent* ev)
return AuTrue;
}
-static AuDeviceID find_device(int nch)
+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:
+ DPRINTF("ao_nas: event_handler(): kind %s state %s->%s reason %s numbytes %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->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
+ nas_data->server_buffer_used = 0;
+ nas_readBuffer(nas_data, nas_data->server_buffer_size - nas_data->server_buffer_used);
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default: // silently ignored
+ break;
+ }
+ break;
+ default:
+ printf("ao_nas: nas_event_handler(): unhandled event type %d\n", ev->type);
+ break;
+ }
+ return AuTrue;
+}
+
+static AuDeviceID nas_find_device(AuServer *aud, int nch)
{
int i;
for (i = 0; i < AuServerNumDevices(aud); i++) {
@@ -174,7 +284,7 @@ static AuDeviceID find_device(int nch)
return AuNone;
}
-static unsigned char aformat_to_auformat(unsigned int format)
+static unsigned char nas_aformat_to_auformat(unsigned int format)
{
switch (format) {
case AFMT_U8: return AuFormatLinearUnsigned8;
@@ -199,29 +309,32 @@ static int init(int rate,int channels,int format,int flags)
{
AuElement elms[3];
AuStatus as;
- unsigned char auformat = aformat_to_auformat(format);
- int bytes_per_sample;
+ unsigned char auformat = nas_aformat_to_auformat(format);
+ int bytes_per_sample = channels * AuSizeofFormat(auformat);
char *server;
+ nas_data=malloc(sizeof(struct ao_nas_data));
+
printf("ao2: %d Hz %d chans %s\n",rate,channels,
- audio_out_format_name(format));
+ audio_out_format_name(format));
if (!auformat) {
- printf("Unsupported format -> nosound\n");
+ printf("ao_nas: init(): Unsupported format -> nosound\n");
return 0;
}
- client_buffer = malloc(BUFFER_SIZE);
- bytes_per_sample = channels * AuSizeofFormat(auformat);
+ nas_data->client_buffer_size = NAS_BUFFER_SIZE;
+ nas_data->client_buffer = malloc(nas_data->client_buffer_size);
+ nas_data->server_buffer_size = NAS_BUFFER_SIZE;
ao_data.samplerate = rate;
ao_data.channels = channels;
- ao_data.buffersize = BUFFER_SIZE * 2;
- ao_data.outburst = FRAG_SIZE;
+ ao_data.buffersize = NAS_BUFFER_SIZE * 2;
+ ao_data.outburst = NAS_FRAG_SIZE;
ao_data.bps = rate * bytes_per_sample;
if (!bytes_per_sample) {
- printf("Zero bytes per sample -> nosound\n");
+ printf("ao_nas: init(): Zero bytes per sample -> nosound\n");
return 0;
}
@@ -231,95 +344,112 @@ static int init(int rate,int channels,int format,int flags)
if (!server) // default to tcp/localhost:8000
server = "tcp/localhost:8000";
- printf("Using audioserver %s\n", server);
+ printf("ao_nas: init(): Using audioserver %s\n", server);
- aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
- if (!aud){
- printf("Can't open nas audio server -> nosound\n");
+ nas_data->aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL);
+ if (!nas_data->aud){
+ printf("ao_nas: init(): Can't open nas audio server -> nosound\n");
return 0;
}
- dev = find_device(channels);
- if ((dev == AuNone) || (!(flow = AuCreateFlow(aud, NULL)))) {
- printf("Can't find a device serving that many channels -> nosound\n");
- AuCloseServer(aud);
- aud = 0;
+ nas_data->dev = nas_find_device(nas_data->aud, channels);
+ if ((nas_data->dev == AuNone) || (!(nas_data->flow = AuCreateFlow(nas_data->aud, NULL)))) {
+ printf("ao_nas: init(): Can't find a device serving that many channels -> nosound\n");
+ AuCloseServer(nas_data->aud);
+ nas_data->aud = 0;
return 0;
}
AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue,
- BUFFER_SIZE / bytes_per_sample,
- (BUFFER_SIZE - FRAG_SIZE) / bytes_per_sample,
+ NAS_BUFFER_SIZE / bytes_per_sample,
+ (NAS_BUFFER_SIZE - NAS_FRAG_SIZE) / bytes_per_sample,
0, NULL);
- AuMakeElementExportDevice(elms+1, 0, dev, rate,
+ AuMakeElementExportDevice(elms+1, 0, nas_data->dev, rate,
AuUnlimitedSamples, 0, NULL);
- AuSetElements(aud, flow, AuTrue, 2, elms, &as);
+ AuSetElements(nas_data->aud, nas_data->flow, AuTrue, 2, elms, &as);
if (as != AuSuccess)
- printf("AuSetElements returned status %d!\n", as);
- AuRegisterEventHandler(aud, AuEventHandlerIDMask |
+ nas_print_error(nas_data->aud, "init(): AuSetElements", as);
+ AuRegisterEventHandler(nas_data->aud, AuEventHandlerIDMask |
AuEventHandlerTypeMask,
- AuEventTypeElementNotify, flow,
- event_handler, (AuPointer) NULL);
- AuSetErrorHandler(aud, error_handler);
- AuStartFlow(aud, flow, &as);
- if (as != AuSuccess)
- printf("AuSetElements returned status %d!\n", as);
+ AuEventTypeElementNotify, nas_data->flow,
+ nas_event_handler, (AuPointer) nas_data);
+ AuSetErrorHandler(nas_data->aud, nas_error_handler);
+ nas_data->flow_stopped=1;
- /*
- * Wait for first buffer underrun event
- *
- * For some weird reason we get a buffer underrun event if we
- * don't fill the server buffer fast enough after staring the
- * flow. So we just wait for it to happen to be in a sane state.
- */
- wait_for_event();
-
- pthread_create(&event_thread, NULL, &event_thread_start, NULL);
+ pthread_mutex_init(&nas_data->buffer_mutex, NULL);
+ pthread_create(&nas_data->event_thread, NULL, &nas_event_thread_start, nas_data);
return 1;
}
// close audio device
static void uninit(){
- stop_thread = 1;
- pthread_join(event_thread, NULL);
- AuStopFlow(aud, flow, NULL);
- AuCloseServer(aud);
- aud = 0;
- free(client_buffer);
+ AuStatus as;
+
+ 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);
}
// stop playing and empty buffers (for seeking/pause)
static void reset(){
- pthread_mutex_lock(&buffer_mutex);
- client_buffer_used = 0;
- pthread_mutex_unlock(&buffer_mutex);
- while (server_buffer_used > 0) {
- usleep(1000);
-// DPRINTF("used=%d\n", server_buffer_used);
+ AuStatus as;
+
+ pthread_mutex_lock(&nas_data->buffer_mutex);
+ nas_data->client_buffer_used = 0;
+ if (!nas_data->flow_stopped) {
+ 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;
}
+ nas_data->server_buffer_used = 0;
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
}
// stop playing, keep buffers (for pause)
static void audio_pause()
{
- // for now, just call reset();
- reset();
+ AuStatus as;
+
+ DPRINTF("ao_nas: audio_pause()\n");
+
+ nas_data->flow_paused = 1;
}
// resume playing, after audio_pause()
static void audio_resume()
{
+ AuStatus as;
+ AuEvent ev;
+
+ DPRINTF("ao_nas: audio_resume()\n");
+
+ nas_data->flow_stopped = 0;
+ nas_data->flow_paused = 0;
+ AuStartFlow(nas_data->aud, nas_data->flow, &as);
+ if (as != AuSuccess)
+ nas_print_error(nas_data->aud, "play(): AuStartFlow", as);
}
+
// return: how many bytes can be played without blocking
static int get_space()
{
int result;
+
+ DPRINTF("ao_nas: get_space()\n");
- pthread_mutex_lock(&buffer_mutex);
- result = client_buffer_size - client_buffer_used;
- pthread_mutex_unlock(&buffer_mutex);
+ pthread_mutex_lock(&nas_data->buffer_mutex);
+ result = nas_data->client_buffer_size - nas_data->client_buffer_used;
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
return result;
}
@@ -327,21 +457,47 @@ static int get_space()
// plays 'len' bytes of 'data'
// it should round it down to outburst*n
// return: number of bytes played
-static int play(void* data,int len,int flags){
- writeBuffer(data, len);
-// printf("ao_nas: wrote %d bytes of audio data\n", len);
- return len;
+static int play(void* data,int len,int flags)
+{
+ int maxbursts, playbursts, writelen;
+ AuStatus as;
+
+ DPRINTF("ao_nas: play()\n");
+
+ if (nas_data->flow_stopped) {
+ AuEvent ev;
+
+ 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;
+ while (!nas_empty_event_queue(nas_data)); // wait for first buffer underrun event
+ }
+
+ pthread_mutex_lock(&nas_data->buffer_mutex);
+ maxbursts = (nas_data->client_buffer_size -
+ nas_data->client_buffer_used) / ao_data.outburst;
+ playbursts = len / ao_data.outburst;
+ writelen = (playbursts > maxbursts ? maxbursts : playbursts) *
+ ao_data.outburst;
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
+
+ nas_writeBuffer(nas_data, data, writelen);
+ return writelen;
}
// return: delay in seconds between first and last sample in buffer
static float get_delay()
{
float result;
-
- pthread_mutex_lock(&buffer_mutex);
- result = ((float)(client_buffer_used + server_buffer_used)) /
- (float)ao_data.bps;
- pthread_mutex_unlock(&buffer_mutex);
+
+ DPRINTF("ao_nas: get_delay()\n");
+
+ pthread_mutex_lock(&nas_data->buffer_mutex);
+ result = ((float)(nas_data->client_buffer_used +
+ nas_data->server_buffer_used)) /
+ (float)ao_data.bps;
+ pthread_mutex_unlock(&nas_data->buffer_mutex);
return result;
}