diff options
author | arpi <arpi@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2001-12-03 01:13:48 +0000 |
---|---|---|
committer | arpi <arpi@b3059339-0415-0410-9bf9-f77b7e298cf2> | 2001-12-03 01:13:48 +0000 |
commit | 0a2d56ae1baedd512a8fe6104124d3d33feabe46 (patch) | |
tree | ad6a296976599a36610c2b5757844b2161f6ce94 /libao2/ao_nas.c | |
parent | e76248a9956aa8822a5ce31a76fc825c512916b6 (diff) | |
download | mpv-0a2d56ae1baedd512a8fe6104124d3d33feabe46.tar.bz2 mpv-0a2d56ae1baedd512a8fe6104124d3d33feabe46.tar.xz |
-ao NAS support by Tobias Diedrich <ranma@gmx.at>
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@3277 b3059339-0415-0410-9bf9-f77b7e298cf2
Diffstat (limited to 'libao2/ao_nas.c')
-rw-r--r-- | libao2/ao_nas.c | 333 |
1 files changed, 333 insertions, 0 deletions
diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c new file mode 100644 index 0000000000..2c9a24e9b2 --- /dev/null +++ b/libao2/ao_nas.c @@ -0,0 +1,333 @@ +#include <stdio.h> +#include <stdlib.h> +#include <pthread.h> +#include <audio/audiolib.h> + +#include "audio_out.h" +#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_DEBUG 0 + +#if NAS_DEBUG == 1 +#define DPRINTF(format, args...) fprintf(stderr, format, ## args); \ + fflush(stderr) +#else +#define DPRINTF(format, args...) +#endif + +static ao_info_t info = +{ + "NAS audio output", + "nas", + "Tobias Diedrich", + "" +}; + +static AuServer* aud; +static AuFlowID flow; +static AuDeviceID dev; + +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; + +pthread_t event_thread; +static int stop_thread; + +LIBAO_EXTERN(nas) + +static void wait_for_event() +{ + AuEvent ev; + + AuNextEvent(aud, AuTrue, &ev); + AuDispatchEvent(aud, &ev); +} + +static int readBuffer(int num) +{ + pthread_mutex_lock(&buffer_mutex); + DPRINTF("readBuffer(): num=%d client=%d/%d server=%d/%d\n", + num, + client_buffer_used, client_buffer_size, + server_buffer_used, server_buffer_size); + + if (client_buffer_used == 0) { + DPRINTF("buffer is empty, nothing read.\n"); + pthread_mutex_unlock(&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); + + return num; +} + +static void writeBuffer(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); + + memcpy(client_buffer + client_buffer_used, data, len); + client_buffer_used += len; + + pthread_mutex_unlock(&buffer_mutex); + if (server_buffer_used < server_buffer_size) + readBuffer(server_buffer_size - server_buffer_used); +} + + +static void *event_thread_start(void *data) +{ + while (!stop_thread) { + wait_for_event(); + } +} + +static AuBool event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd) +{ + 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); + + 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; +} + +static AuBool error_handler(AuServer* aud, AuErrorEvent* ev) +{ + char s[100]; + AuGetErrorText(aud, ev->error_code, s, 100); + fprintf(stderr,"libaudiooss: error [%s]\n" + "error_code: %d\n" + "request_code: %d\n" + "minor_code: %d\n", + s, + ev->error_code, + ev->request_code, + ev->minor_code); + fflush(stderr); + + return AuTrue; +} + +static AuDeviceID find_device(int nch) +{ + int i; + for (i = 0; i < AuServerNumDevices(aud); i++) { + AuDeviceAttributes *dev = AuServerDevice(aud, i); + if ((AuDeviceKind(dev) == AuComponentKindPhysicalOutput) && + AuDeviceNumTracks(dev) == nch) { + return AuDeviceIdentifier(dev); + } + } + return AuNone; +} + +static unsigned char aformat_to_auformat(unsigned int format) +{ + switch (format) { + case AFMT_U8: return AuFormatLinearUnsigned8; + case AFMT_S8: return AuFormatLinearSigned8; + case AFMT_U16_LE: return AuFormatLinearUnsigned16LSB; + case AFMT_U16_BE: return AuFormatLinearUnsigned16MSB; + case AFMT_S16_LE: return AuFormatLinearSigned16LSB; + case AFMT_S16_BE: return AuFormatLinearSigned16MSB; + case AFMT_MU_LAW: return AuFormatULAW8; + default: return 0; + } +} + +// to set/get/query special features/parameters +static int control(int cmd,int arg){ + return -1; +} + +// open & setup audio device +// return: 1=success 0=fail +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; + char *server; + + printf("ao2: %d Hz %d chans %s\n",rate,channels, + audio_out_format_name(format)); + + if (!auformat) { + printf("Unsupported format -> nosound\n"); + return 0; + } + + client_buffer = malloc(BUFFER_SIZE); + bytes_per_sample = channels * AuSizeofFormat(auformat); + + ao_data.samplerate = rate; + ao_data.channels = channels; + ao_data.buffersize = BUFFER_SIZE * 2; + ao_data.outburst = FRAG_SIZE; + ao_data.bps = rate * bytes_per_sample; + + if (!bytes_per_sample) { + printf("Zero bytes per sample -> nosound\n"); + return 0; + } + + if (!(server = getenv("AUDIOSERVER"))) + server = getenv("DISPLAY"); + + if (!server) // default to tcp/localhost:8000 + server = "tcp/localhost:8000"; + + printf("Using audioserver %s\n", server); + + aud = AuOpenServer(server, 0, NULL, 0, NULL, NULL); + if (!aud){ + printf("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; + return 0; + } + + AuMakeElementImportClient(elms, rate, auformat, channels, AuTrue, + BUFFER_SIZE / bytes_per_sample, + (BUFFER_SIZE - FRAG_SIZE) / bytes_per_sample, + 0, NULL); + AuMakeElementExportDevice(elms+1, 0, dev, rate, + AuUnlimitedSamples, 0, NULL); + AuSetElements(aud, flow, AuTrue, 2, elms, &as); + if (as != AuSuccess) + printf("AuSetElements returned status %d!\n", as); + AuRegisterEventHandler(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); + + /* + * 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); + + 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); +} + +// 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); + } +} + +// stop playing, keep buffers (for pause) +static void audio_pause() +{ + // for now, just call reset(); + reset(); +} + +// resume playing, after audio_pause() +static void audio_resume() +{ +} + +// return: how many bytes can be played without blocking +static int get_space() +{ + int result; + + pthread_mutex_lock(&buffer_mutex); + result = client_buffer_size - client_buffer_used; + pthread_mutex_unlock(&buffer_mutex); + + return result; +} + +// 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; +} + +// 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); + + return result; +} |