diff options
Diffstat (limited to 'libao2')
-rw-r--r-- | libao2/ao_coreaudio.c | 3 | ||||
-rw-r--r-- | libao2/ao_dsound.c | 70 | ||||
-rw-r--r-- | libao2/ao_ivtv.c | 161 | ||||
-rw-r--r-- | libao2/ao_mpegpes.c | 336 | ||||
-rw-r--r-- | libao2/ao_nas.c | 646 | ||||
-rw-r--r-- | libao2/ao_openal.c | 30 | ||||
-rw-r--r-- | libao2/ao_oss.c | 3 | ||||
-rw-r--r-- | libao2/ao_pulse.c | 7 | ||||
-rw-r--r-- | libao2/ao_sdl.c | 319 | ||||
-rw-r--r-- | libao2/ao_sun.c | 692 | ||||
-rw-r--r-- | libao2/ao_win32.c | 326 | ||||
-rw-r--r-- | libao2/audio_out.c | 35 | ||||
-rw-r--r-- | libao2/audio_out.h | 1 |
13 files changed, 81 insertions, 2548 deletions
diff --git a/libao2/ao_coreaudio.c b/libao2/ao_coreaudio.c index d1a93c85e2..ff077b81b6 100644 --- a/libao2/ao_coreaudio.c +++ b/libao2/ao_coreaudio.c @@ -44,6 +44,7 @@ #include <inttypes.h> #include <sys/types.h> #include <unistd.h> +#include <endian.h> #include "config.h" #include "mp_msg.h" @@ -820,7 +821,7 @@ static int OpenSPDIF(void) /* FIXME: If output stream is not native byte-order, we need change endian somewhere. */ /* Although there's no such case reported. */ -#if HAVE_BIGENDIAN +#if BYTE_ORDER == BIG_ENDIAN if (!(ao->stream_format.mFormatFlags & kAudioFormatFlagIsBigEndian)) #else /* tell mplayer that we need a byteswap on AC3 streams, */ diff --git a/libao2/ao_dsound.c b/libao2/ao_dsound.c index b33f2949fd..f1fc0dd00a 100644 --- a/libao2/ao_dsound.c +++ b/libao2/ao_dsound.c @@ -121,8 +121,10 @@ static int min_free_space = 0; ///if the free space is below this val ///there will always be at least this amout of free space to prevent ///get_space() from returning wrong values when buffer is 100% full. ///will be replaced with nBlockAlign in init() +static int underrun_check = 0; ///0 or last reported free space (underrun detection) static int device_num = 0; ///wanted device number static GUID device; ///guid of the device +static int audio_volume; /***************************************************************************************/ @@ -314,6 +316,8 @@ static int write_buffer(unsigned char *data, int len) LPVOID lpvPtr2; DWORD dwBytes2; + underrun_check = 0; + // Lock the buffer res = IDirectSoundBuffer_Lock(hdsbuf,write_offset, len, &lpvPtr1, &dwBytes1, &lpvPtr2, &dwBytes2, 0); // If the buffer was lost, restore and retry lock. @@ -391,16 +395,16 @@ static int control(int cmd, void *arg) switch (cmd) { case AOCONTROL_GET_VOLUME: { ao_control_vol_t* vol = (ao_control_vol_t*)arg; - IDirectSoundBuffer_GetVolume(hdsbuf, &volume); - vol->left = vol->right = pow(10.0, (float)(volume+10000) / 5000.0); - //printf("ao_dsound: volume: %f\n",vol->left); + vol->left = vol->right = audio_volume; return CONTROL_OK; } case AOCONTROL_SET_VOLUME: { ao_control_vol_t* vol = (ao_control_vol_t*)arg; - volume = (DWORD)(log10(vol->right) * 5000.0) - 10000; + volume = audio_volume = vol->right; + if (volume < 1) + volume = 1; + volume = (DWORD)(log10(volume) * 5000.0) - 10000; IDirectSoundBuffer_SetVolume(hdsbuf, volume); - //printf("ao_dsound: volume: %f\n",vol->left); return CONTROL_OK; } } @@ -421,6 +425,7 @@ static int init(int rate, int channels, int format, int flags) if (!InitDirectSound()) return 0; global_ao->no_persistent_volume = true; + audio_volume = 100; // ok, now create the buffers WAVEFORMATEXTENSIBLE wformat; @@ -544,6 +549,7 @@ static void reset(void) // reset directsound buffer IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0); write_offset=0; + underrun_check=0; } /** @@ -568,22 +574,16 @@ static void audio_resume(void) */ static void uninit(int immed) { - if(immed)reset(); - else{ - DWORD status; - IDirectSoundBuffer_Play(hdsbuf, 0, 0, 0); - while(!IDirectSoundBuffer_GetStatus(hdsbuf,&status) && (status&DSBSTATUS_PLAYING)) - usec_sleep(20000); - } + if (!immed) + usec_sleep(get_delay() * 1000000); + reset(); + DestroyBuffer(); UninitDirectSound(); } -/** -\brief find out how many bytes can be written into the audio buffer without -\return free space in bytes, has to return 0 if the buffer is almost full -*/ -static int get_space(void) +// return exact number of free (safe to write) bytes +static int check_free_buffer_size(void) { int space; DWORD play_offset; @@ -595,6 +595,28 @@ static int get_space(void) // write_cursor is the position after which it is assumed to be save to write data // write_offset is the postion where we actually write the data to if(space > buffer_size)space -= buffer_size; // write_offset < play_offset + // Check for buffer underruns. An underrun happens if DirectSound + // started to play old data beyond the current write_offset. Detect this + // by checking whether the free space shrinks, even though no data was + // written (i.e. no write_buffer). Doesn't always work, but the only + // reason we need this is to deal with the situation when playback ends, + // and the buffer is only half-filled. + if (space < underrun_check) { + // there's no useful data in the buffers + space = buffer_size; + reset(); + } + underrun_check = space; + return space; +} + +/** +\brief find out how many bytes can be written into the audio buffer without +\return free space in bytes, has to return 0 if the buffer is almost full +*/ +static int get_space(void) +{ + int space = check_free_buffer_size(); if(space < min_free_space)return 0; return space-min_free_space; } @@ -608,13 +630,7 @@ static int get_space(void) */ static int play(void* data, int len, int flags) { - DWORD play_offset; - int space; - - // make sure we have enough space to write data - IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL); - space=buffer_size-(write_offset-play_offset); - if(space > buffer_size)space -= buffer_size; // write_offset < play_offset + int space = check_free_buffer_size(); if(space < len) len = space; if (!(flags & AOPLAY_FINAL_CHUNK)) @@ -628,10 +644,6 @@ static int play(void* data, int len, int flags) */ static float get_delay(void) { - DWORD play_offset; - int space; - IDirectSoundBuffer_GetCurrentPosition(hdsbuf,&play_offset,NULL); - space=play_offset-write_offset; - if(space <= 0)space += buffer_size; + int space = check_free_buffer_size(); return (float)(buffer_size - space) / (float)ao_data.bps; } diff --git a/libao2/ao_ivtv.c b/libao2/ao_ivtv.c deleted file mode 100644 index e05537cbd8..0000000000 --- a/libao2/ao_ivtv.c +++ /dev/null @@ -1,161 +0,0 @@ -/* - * audio output for WinTV PVR-150/250/350 (a.k.a IVTV) cards - * through Connexant hardware MPEG decoder - * See http://ivtvdriver.org/index.php/Main_Page for more details on the - * cards supported by the ivtv driver. - * - * WARNING: You need to force -ac hwmpa for audio output to work. - * - * Copyright (C) 2006 Benjamin Zores - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <inttypes.h> - -#include "config.h" - -#include "mp_msg.h" - -#include "audio_out.h" -#include "audio_out_internal.h" -#include "libaf/af_format.h" -#include "libmpdemux/mpeg_packetizer.h" -#include "libvo/vo_ivtv.h" - -#define MPEG_AUDIO_ID 0x1C0 - -static int freq = 0; - -static const ao_info_t info = -{ - "IVTV MPEG Audio Decoder output", - "ivtv", - "Benjamin Zores", - "" -}; - -LIBAO_EXTERN(ivtv) - -/* to set/get/query special features/parameters */ -static int -control (int cmd,void *arg) -{ - return CONTROL_UNKNOWN; -} - -/* open & setup audio device */ -static int -init (int rate, int channels, int format, int flags) -{ - if (ivtv_fd < 0) - return 0; - - if (format != AF_FORMAT_MPEG2) - { - mp_msg (MSGT_AO, MSGL_FATAL, - "AO: [ivtv] can only handle MPEG audio streams.\n"); - return 0; - } - - ao_data.outburst = 2048; - ao_data.samplerate = rate; - ao_data.channels = channels; - ao_data.format = AF_FORMAT_MPEG2; - ao_data.buffersize = 2048; - ao_data.bps = rate * 2 * 2; - ao_data.brokenpts = 0; - freq = rate; - - /* check for supported audio rate */ - if (rate != 32000 || rate != 41000 || rate != 48000) - { - mp_tmsg (MSGT_AO, MSGL_ERR, "[AO MPEGPES] %d Hz not supported, try to resample.\n", rate); - rate = 48000; - } - - return 1; -} - -/* close audio device */ -static void -uninit (int immed) -{ - /* nothing to do */ -} - -/* stop playing and empty buffers (for seeking/pause) */ -static void -reset (void) -{ - /* nothing to do */ -} - -/* stop playing, keep buffers (for pause) */ -static void -audio_pause (void) -{ - reset (); -} - -/* resume playing, after audio_pause() */ -static void -audio_resume (void) -{ - /* nothing to do */ -} - -/* how many bytes can be played without blocking */ -static int -get_space (void) -{ - extern int vo_pts; - float x; - int y; - - x = (float) (vo_pts - ao_data.brokenpts) / 90000.0; - if (x <= 0) - return 0; - - y = freq * 4 * x; - y /= ao_data.outburst; - y *= ao_data.outburst; - - if (y > 32000) - y = 32000; - - return y; -} - -/* number of bytes played */ -static int -play (void *data, int len, int flags) -{ - if (ao_data.format != AF_FORMAT_MPEG2) - return 0; - - send_mpeg_pes_packet (data, len, MPEG_AUDIO_ID, ao_data.brokenpts, 2, ivtv_write); - - return len; -} - -/* delay in seconds between first and last sample in buffer */ -static float -get_delay (void) -{ - return 0.0; -} diff --git a/libao2/ao_mpegpes.c b/libao2/ao_mpegpes.c deleted file mode 100644 index fe3f20fc85..0000000000 --- a/libao2/ao_mpegpes.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * MPEG-PES audio output driver - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <inttypes.h> -#include <errno.h> - -#include "config.h" - -#include "audio_out.h" -#include "audio_out_internal.h" - -#include "libaf/af_format.h" -#include "libmpdemux/mpeg_packetizer.h" -#include "subopt-helper.h" - -#include "mp_msg.h" - -#ifdef CONFIG_DVB -#include <poll.h> -#include <sys/ioctl.h> -#include <linux/dvb/audio.h> -audio_mixer_t dvb_mixer={255,255}; -#endif - -#define true 1 -#define false 0 - -extern int vo_mpegpes_fd; -int ao_mpegpes_fd = -1; - -#include <errno.h> - -static const ao_info_t info = -{ -#ifdef CONFIG_DVB - "DVB audio output", -#else - "MPEG-PES audio output", -#endif - "mpegpes", - "A'rpi", - "" -}; - -LIBAO_EXTERN(mpegpes) - - -// to set/get/query special features/parameters -static int control(int cmd,void *arg){ -#ifdef CONFIG_DVB - switch(cmd){ - case AOCONTROL_GET_VOLUME: - if(ao_mpegpes_fd >= 0){ - ((ao_control_vol_t*)(arg))->left=dvb_mixer.volume_left/2.56; - ((ao_control_vol_t*)(arg))->right=dvb_mixer.volume_right/2.56; - return CONTROL_OK; - } - return CONTROL_ERROR; - case AOCONTROL_SET_VOLUME: - if(ao_mpegpes_fd >= 0){ - dvb_mixer.volume_left=((ao_control_vol_t*)(arg))->left*2.56; - dvb_mixer.volume_right=((ao_control_vol_t*)(arg))->right*2.56; - if(dvb_mixer.volume_left>255) dvb_mixer.volume_left=255; - if(dvb_mixer.volume_right>255) dvb_mixer.volume_right=255; - // printf("Setting DVB volume: %d ; %d \n",dvb_mixer.volume_left,dvb_mixer.volume_right); - if ( (ioctl(vo_mpegpes_fd,AUDIO_SET_MIXER, &dvb_mixer) < 0)){ - mp_tmsg(MSGT_AO, MSGL_ERR, "[AO MPEGPES] DVB audio set mixer failed: %s.\n", - strerror(errno)); - return CONTROL_ERROR; - } - return CONTROL_OK; - } - return CONTROL_ERROR; - } -#endif - return CONTROL_UNKNOWN; -} - -static int freq=0; -static int freq_id=0; - -#ifdef CONFIG_DVB -static int init_device(int card) -{ - char ao_file[30]; - sprintf(ao_file, "/dev/dvb/adapter%d/audio0", card); - mp_msg(MSGT_VO,MSGL_INFO, "Opening %s\n", ao_file); - if((ao_mpegpes_fd = open(ao_file,O_RDWR|O_NONBLOCK)) < 0) - { - mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO DEVICE: %s\n", strerror(errno)); - return -1; - } - if( (ioctl(ao_mpegpes_fd, AUDIO_SELECT_SOURCE, AUDIO_SOURCE_MEMORY) < 0)) - { - mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SELECT SOURCE: %s\n", strerror(errno)); - goto fail; - } - if((ioctl(ao_mpegpes_fd, AUDIO_PLAY) < 0)) - { - mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO PLAY: %s\n", strerror(errno)); - goto fail; - } - if((ioctl(ao_mpegpes_fd, AUDIO_SET_AV_SYNC, true) < 0)) - { - mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SET AV SYNC: %s\n", strerror(errno)); - goto fail; - } - //FIXME: in vo_mpegpes audio was initialized as MUTEd - if((ioctl(ao_mpegpes_fd, AUDIO_SET_MUTE, false) < 0)) - { - mp_msg(MSGT_VO, MSGL_ERR, "DVB AUDIO SET MUTE: %s\n", strerror(errno)); - goto fail; - } - return ao_mpegpes_fd; -fail: - close(ao_mpegpes_fd); - ao_mpegpes_fd = -1; - return -1; -} -#endif - -static int preinit(const char *arg) -{ - int card = -1; - char *ao_file = NULL; - - const opt_t subopts[] = { - {"card", OPT_ARG_INT, &card, NULL}, - {"file", OPT_ARG_MSTRZ, &ao_file, NULL}, - {NULL} - }; - - if(subopt_parse(ao_subdevice, subopts) != 0) - { - mp_msg(MSGT_VO, MSGL_ERR, "AO_MPEGPES, Unrecognized options\n"); - return -1; - } - if(card==-1) - { - //search the first usable card - int n; - char file[30]; - for(n=0; n<4; n++) - { - sprintf(file, "/dev/dvb/adapter%d/audio0", n); - if(access(file, F_OK | W_OK)==0) - { - card = n+1; - break; - } - } - } - if((card < 1) || (card > 4)) - { - mp_msg(MSGT_VO, MSGL_ERR, "DVB card number must be between 1 and 4\n"); - return -1; - } - card--; - -#ifdef CONFIG_DVB - if(!ao_file) - return init_device(card); -#else - if(!ao_file) - return vo_mpegpes_fd; //video fd -#endif - - ao_mpegpes_fd = open(ao_file, O_WRONLY | O_CREAT, 0666); - if(ao_mpegpes_fd < 0) - { - mp_msg(MSGT_VO, MSGL_ERR, "ao_mpegpes: %s\n", strerror(errno)); - return -1; - } - return ao_mpegpes_fd; -} - -static int my_ao_write(const unsigned char* data,int len){ - int orig_len = len; -#ifdef CONFIG_DVB -#define NFD 1 - struct pollfd pfd[NFD]; - - pfd[0].fd = ao_mpegpes_fd; - pfd[0].events = POLLOUT; - - while(len>0){ - if(poll(pfd,NFD,1)){ - if(pfd[0].revents & POLLOUT){ - int ret = write(ao_mpegpes_fd, data, len); - if(ret<=0){ - mp_msg(MSGT_VO, MSGL_ERR, "ao_mpegpes write: %s\n", strerror(errno)); - usleep(0); - } else { - len-=ret; - data+=ret; - } - } else usleep(1000); - } - } - -#else - if(ao_mpegpes_fd < 0) return 0; // no file - write(ao_mpegpes_fd, data, len); // write to file -#endif - return orig_len; -} - - -// open & setup audio device -// return: 1=success 0=fail -static int init(int rate,int channels,int format,int flags){ - if(preinit(NULL)<0) return 0; - - ao_data.channels=2; - ao_data.outburst=2000; - switch(format){ - case AF_FORMAT_S16_BE: - case AF_FORMAT_MPEG2: - case AF_FORMAT_AC3_BE: - ao_data.format=format; - break; - case AF_FORMAT_AC3_LE: - ao_data.format=AF_FORMAT_AC3_BE; - break; - default: - ao_data.format=AF_FORMAT_S16_BE; - } - - switch(rate){ - case 48000: freq_id=0;break; - case 96000: freq_id=1;break; - case 44100: freq_id=2;break; - case 32000: freq_id=3;break; - default: - mp_tmsg(MSGT_AO, MSGL_ERR, "[AO MPEGPES] %d Hz not supported, try to resample.\n", rate); -#if 0 - if(rate>48000) rate=96000; else - if(rate>44100) rate=48000; else - if(rate>32000) rate=44100; else - rate=32000; - goto retry; -#else - rate=48000; freq_id=0; -#endif - } - - ao_data.bps=rate*2*2; - freq=ao_data.samplerate=rate; - - return 1; -} - -// close audio device -static void uninit(int immed){ - if (ao_mpegpes_fd >= 0) - close(ao_mpegpes_fd); - ao_mpegpes_fd = -1; -} - -// stop playing and empty buffers (for seeking/pause) -static void reset(void){ - -} - -// stop playing, keep buffers (for pause) -static void audio_pause(void) -{ - // for now, just call reset(); - reset(); -} - -// resume playing, after audio_pause() -static void audio_resume(void) -{ -} - -extern int vo_pts; - -// return: how many bytes can be played without blocking -static int get_space(void){ - float x=(float)(vo_pts-ao_data.brokenpts)/90000.0; - int y; - //FIXME: is it correct? - if(vo_mpegpes_fd < 0) return 32000; //not using -vo mpegpes -// printf("vo_pts: %5.3f ao_pts: %5.3f\n",vo_pts/90000.0,ao_data.brokenpts/90000.0); - if(x<=0) return 0; - y=freq*4*x;y/=ao_data.outburst;y*=ao_data.outburst; - if(y>32000) y=32000; -// printf("diff: %5.3f -> %d \n",x,y); - return y; -} - -// 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){ -// printf("\nao_mpegpes: play(%d) freq=%d\n",len,freq_id); - if(ao_data.format==AF_FORMAT_MPEG2) - send_mpeg_pes_packet (data, len, 0x1C0, ao_data.brokenpts, 1, my_ao_write); - else { -// if(len>2000) len=2000; -// printf("ao_mpegpes: len=%d \n",len); - send_mpeg_lpcm_packet(data, len, 0xA0, ao_data.brokenpts, freq_id, my_ao_write); - } - return len; -} - -// return: delay in seconds between first and last sample in buffer -static float get_delay(void){ - - return 0.0; -} diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c deleted file mode 100644 index d3274df9a5..0000000000 --- a/libao2/ao_nas.c +++ /dev/null @@ -1,646 +0,0 @@ -/* - * NAS audio output driver - * - * copyright (c) 2001 Tobias Diedrich <ranma@gmx.at> - * - * Based on the libaudiooss parts rewritten by me, which were - * originally based on the NAS output plugin for XMMS. - * - * XMMS plugin by Willem Monsuwe - * adapted for libaudiooss by Jon Trulson - * further modified by Erik Inge Bolsø - * largely rewritten and used for this ao driver by Tobias Diedrich - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -/* - * 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 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 - * falls below the low watermark value. The event handler gets called - * again and refills the buffer by the number of bytes requested by the - * server (usually a multiple of 4096). To prevent stuttering on - * startup (start of playing, seeks, unpausing) the client buffer should - * be bigger than the server buffer. (For debugging we also do some - * 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 <limits.h> -#include <audio/audiolib.h> - -#include "config.h" -#include "mp_msg.h" - -#include "audio_out.h" -#include "audio_out_internal.h" -#include "libaf/af_format.h" - -/* NAS_FRAG_SIZE must be a power-of-two value */ -#define NAS_FRAG_SIZE 4096 - -static const char * const nas_event_types[] = { - "Undefined", - "Undefined", - "ElementNotify", - "GrabNotify", - "MonitorNotify", - "BucketNotify", - "DeviceNotify" -}; - -static const char * const nas_elementnotify_kinds[] = { - "LowWater", - "HighWater", - "State", - "Unknown" -}; - -static const char * const nas_states[] = { - "Stop", - "Start", - "Pause", - "Any" -}; - -static const char * const nas_reasons[] = { - "User", - "Underrun", - "Overrun", - "EOF", - "Watermark", - "Hardware", - "Any" -}; - -static const char* nas_reason(unsigned int reason) -{ - if (reason > 6) reason = 6; - return nas_reasons[reason]; -} - -static const char* nas_elementnotify_kind(unsigned int kind) -{ - if (kind > 2) kind = 3; - return nas_elementnotify_kinds[kind]; -} - -static const char* nas_event_type(unsigned int type) { - if (type > 6) type = 0; - return nas_event_types[type]; -} - -static const char* nas_state(unsigned int state) { - if (state>3) state = 3; - return nas_states[state]; -} - -static const ao_info_t info = -{ - "NAS audio output", - "nas", - "Tobias Diedrich <ranma+mplayer@tdiedrich.de>", - "" -}; - -struct ao_nas_data { - AuServer *aud; - AuFlowID flow; - AuDeviceID dev; - AuFixedPoint gain; - - unsigned int state; - int expect_underrun; - - char *client_buffer; - char *server_buffer; - unsigned int client_buffer_size; - unsigned int client_buffer_used; - unsigned int server_buffer_size; - unsigned int server_buffer_used; - pthread_mutex_t buffer_mutex; - - pthread_t event_thread; - int stop_thread; -}; - -static struct ao_nas_data *nas_data; - -LIBAO_EXTERN(nas) - -static void nas_print_error(AuServer *aud, const char *prefix, AuStatus as) -{ - char s[100]; - AuGetErrorText(aud, as, s, 100); - mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: %s: returned status %d (%s)\n", prefix, as, s); -} - -static int nas_readBuffer(struct ao_nas_data *nas_data, unsigned int num) -{ - AuStatus as; - - pthread_mutex_lock(&nas_data->buffer_mutex); - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: nas_readBuffer(): num=%d client=%d/%d server=%d/%d\n", - num, - nas_data->client_buffer_used, nas_data->client_buffer_size, - nas_data->server_buffer_used, nas_data->server_buffer_size); - - if (nas_data->client_buffer_used == 0) { - mp_msg(MSGT_AO, MSGL_DBG2, "ao_nas: buffer is empty, nothing read.\n"); - pthread_mutex_unlock(&nas_data->buffer_mutex); - return 0; - } - if (num > nas_data->client_buffer_used) - num = nas_data->client_buffer_used; - - /* - * It is not appropriate to call AuWriteElement() here because the - * buffer is locked and delays writing to the network will cause - * other threads to block waiting for buffer_mutex. Instead the - * data is copied to "server_buffer" and written to the network - * outside of the locked section of code. - * - * (Note: Rather than these two buffers, a single circular buffer - * could eliminate the memcpy/memmove steps.) - */ - /* make sure we don't overflow the buffer */ - if (num > nas_data->server_buffer_size) - num = nas_data->server_buffer_size; - memcpy(nas_data->server_buffer, nas_data->client_buffer, num); - - 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); - - /* - * Now write the new buffer to the network. - */ - 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); - - return num; -} - -static int nas_writeBuffer(struct ao_nas_data *nas_data, void *data, unsigned int len) -{ - pthread_mutex_lock(&nas_data->buffer_mutex); - mp_msg(MSGT_AO, MSGL_DBG2, "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); - - /* make sure we don't overflow the buffer */ - if (len > nas_data->client_buffer_size - nas_data->client_buffer_used) - len = nas_data->client_buffer_size - nas_data->client_buffer_used; - memcpy(nas_data->client_buffer + nas_data->client_buffer_used, data, len); - nas_data->client_buffer_used += len; - - pthread_mutex_unlock(&nas_data->buffer_mutex); - - return len; -} - -static int nas_empty_event_queue(struct ao_nas_data *nas_data) -{ - AuEvent ev; - int result = 0; - - while (AuScanForTypedEvent(nas_data->aud, AuEventsQueuedAfterFlush, - AuTrue, AuEventTypeElementNotify, &ev)) { - AuDispatchEvent(nas_data->aud, &ev); - result = 1; - } - return result; -} - -static void *nas_event_thread_start(void *data) -{ - struct ao_nas_data *nas_data = data; - - 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(1000); - } while (!nas_data->stop_thread); - - return NULL; -} - -static AuBool nas_error_handler(AuServer* aud, AuErrorEvent* ev) -{ - char s[100]; - AuGetErrorText(aud, ev->error_code, s, 100); - mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: 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); - - return AuTrue; -} - -static AuBool nas_event_handler(AuServer *aud, AuEvent *ev, AuEventHandlerRec *hnd) -{ - AuElementNotifyEvent *event = (AuElementNotifyEvent *) ev; - struct ao_nas_data *nas_data = hnd->data; - - 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), - (int)event->num_bytes, - nas_data->expect_underrun); - - if (event->num_bytes > INT_MAX) { - mp_msg(MSGT_AO, MSGL_ERR, "ao_nas: num_bytes > 2GB, server buggy?\n"); - } - - if (event->num_bytes > nas_data->server_buffer_used) - event->num_bytes = nas_data->server_buffer_used; - nas_data->server_buffer_used -= event->num_bytes; - - 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 { - static int hint = 1; - mp_msg(MSGT_AO, MSGL_WARN, - "ao_nas: Buffer underrun.\n"); - if (hint) { - hint = 0; - mp_msg(MSGT_AO, MSGL_HINT, - "Possible reasons are:\n" - "1) Network congestion.\n" - "2) Your NAS server is too slow.\n" - "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(aud, nas_data->flow, NULL); - break; - default: - break; - } - nas_data->state=event->cur_state; - return AuTrue; -} - -static AuDeviceID nas_find_device(AuServer *aud, 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 int nas_aformat_to_auformat(unsigned int *format) -{ - switch (*format) { - case AF_FORMAT_U8: - return AuFormatLinearUnsigned8; - case AF_FORMAT_S8: - return AuFormatLinearSigned8; - case AF_FORMAT_U16_LE: - return AuFormatLinearUnsigned16LSB; - case AF_FORMAT_U16_BE: - return AuFormatLinearUnsigned16MSB; |